Wie wird die Adresse eines Ethereum-Kontrakts berechnet? Welche Anwendungsfälle gibt es, um die Adresse eines Vertrags im Voraus zu kennen?
EDIT April 2019 : CREATE2
Informationen hinzugefügt.
BEARBEITEN Januar 2022 : Solidity-Syntax auf ^0.8.0 aktualisiert.
Die Adresse für einen Ethereum-Vertrag wird deterministisch aus der Adresse seines Erstellers ( sender
) und wie viele Transaktionen der Ersteller gesendet hat ( nonce
) berechnet. Die sender
und nonce
werden RLP-codiert und dann mit Keccak-256 gehasht .
Aus Pyethereum:
def mk_contract_address(sender, nonce):
return sha3(rlp.encode([normalize_address(sender), nonce]))[12:]
In Solidität:
nonce0= address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80))))));
nonce1= address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x01))))));
Beispiel mit etwas Diskussion:
Für den Absender 0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0 werden die folgenden Vertragsadressen erstellt:
nonce0= "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d"
nonce1= "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8"
nonce2= "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91"
nonce3= "0xfffd933a0bc612844eaf0c6fe3e5b8e9b6c1d19c"
In Java mit Web3j:
private String calculateContractAddress(String address, long nonce){
byte[] addressAsBytes = Numeric.hexStringToByteArray(address);
byte[] calculatedAddressAsBytes =
Hash.sha3(RlpEncoder.encode(
new RlpList(
RlpString.create(addressAsBytes),
RlpString.create((nonce)))));
calculatedAddressAsBytes = Arrays.copyOfRange(calculatedAddressAsBytes,
12, calculatedAddressAsBytes.length);
String calculatedAddressAsHex = Numeric.toHexString(calculatedAddressAsBytes);
return calculatedAddressAsHex;
}
Hinweis : Gemäß EIP 161 A Spezifikation werden Vertragskonten mit Nonce = 1 (im Mainnet) initiiert. Daher wird die erste Vertragsadresse, die von einem anderen Vertrag erstellt wurde, mit einer Nonce ungleich Null berechnet.
CREATE2
In EIP-1014 wurde ein neuer Opcode CREATE2
hinzugefügt , der eine weitere Möglichkeit darstellt, wie ein Vertrag erstellt werden kann.
Für den Vertrag, der von CREATE2
seiner Adresse erstellt wird, wird es sein:
keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12:]
Weitere Informationen werden hier hinzugefügt und für die Zwischenzeit siehe EIP-1014 .
address(uint160(uint256(keccak256(abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(0x80))))))
(for nonce0) verwenden.Dank der Antwort von eth hat es sehr geholfen, das 2000-Dollar-Problem zu lösen.
Gerade Problem mit Geldern gelöst, die im Ethereum-Hauptnetzwerk an die Adresse des Smart Contract gesendet wurden, um das Ethereum-Netzwerk zu testen . Wir haben dieselbe Brieftasche mehrmals verwendet, um verschiedene Smart Contracts im Ethereum-Hauptnetzwerk bereitzustellen, bis das Transaktionsfeld nonce den gleichen Wert 13 erreichte, wie er für die Bereitstellung im Testnetzwerk verwendet wurde. Wir haben eine spezielle Methode für neu bereitgestellte Smart Contracts aufgerufen, um Gelder zurückzufordern. Also wurde Smart Contract eingesetzt, nachdem es wirklich finanziert wurde: https://etherscan.io/address/0x9c86825280b1d6c7dB043D4CC86E1549990149f9
Habe gerade einen Artikel zu diesem Thema fertiggestellt: https://medium.com/@k06a/how-we-sent-eth-to-the-wrong-address-and-successfully-recovered-them-2fc18e09d8f6
Hier ist ein node.js-Skript, das eine Ethereum-Vertragsadresse anhand der öffentlichen Adresse und des Nonce-Werts des Vertragserstellers deterministisch berechnet.
Bei Fragen zu Eingaben etc.
// node version: v9.10.0
// module versions:
// rlp@2.0.0
// keccak@1.4.0
const rlp = require("rlp");
const keccak = require("keccak");
var nonce = 0x00; //The nonce must be a hex literal!
var sender = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"; //Requires a hex string as input!
var input_arr = [sender, nonce];
var rlp_encoded = rlp.encode(input_arr);
var contract_address_long = keccak("keccak256")
.update(rlp_encoded)
.digest("hex");
var contract_address = contract_address_long.substring(24); //Trim the first 24 characters.
console.log("contract_address: " + contract_address);
Beachten Sie, dass die Nonce normal inkrementiert werden kann, denken Sie nur daran, dass es sich um einen Hex-Wert handelt.
Ausgabe (nonce = 0x00):
contract_address: cd234a471b72ba2f1ccf0a70fcaba648a5eecd8d
Ausgabe (nonce = 0x01):
contract_address: 343c43a37d37dff08ae8c4a11544c718abb4fcf8
rlp
?Hier ist die aktualisierte Python-Version für moderne Ethereum-Bibliotheken ( eth-utils
):
import rlp
from eth_utils import keccak, to_checksum_address, to_bytes
def mk_contract_address(sender: str, nonce: int) -> str:
"""Create a contract address using eth-utils.
# https://ethereum.stackexchange.com/a/761/620
"""
sender_bytes = to_bytes(hexstr=sender)
raw = rlp.encode([sender_bytes, nonce])
h = keccak(raw)
address_bytes = h[12:]
return to_checksum_address(address_bytes)
print(to_checksum_address(mk_contract_address(to_checksum_address("0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"), 1)))
print("0x343c43a37d37dff08ae8c4a11544c718abb4fcf8")
assert mk_contract_address(to_checksum_address("0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"), 1) == \
to_checksum_address("0x343c43a37d37dff08ae8c4a11544c718abb4fcf8")
RLP in Solidität durchgeführt (habe dies jedoch nicht getestet, Vorsicht! nur zum Verständnis):
function addressFrom(address _origin, uint _nonce) public pure returns (address) {
if(_nonce == 0x00) return address(keccak256(byte(0xd6), byte(0x94), _origin, byte(0x80)));
if(_nonce <= 0x7f) return address(keccak256(byte(0xd6), byte(0x94), _origin, byte(_nonce)));
if(_nonce <= 0xff) return address(keccak256(byte(0xd7), byte(0x94), _origin, byte(0x81), uint8(_nonce)));
if(_nonce <= 0xffff) return address(keccak256(byte(0xd8), byte(0x94), _origin, byte(0x82), uint16(_nonce)));
if(_nonce <= 0xffffff) return address(keccak256(byte(0xd9), byte(0x94), _origin, byte(0x83), uint24(_nonce)));
return address(keccak256(byte(0xda), byte(0x94), _origin, byte(0x84), uint32(_nonce))); // more than 2^32 nonces not realistic
}
Hier ist eine reine ethers.js-Implementierung in TypeScript, die eine Adresse mit Prüfsumme zurückgibt. nonce wird voraussichtlich regelmäßig sein number
.
(ethers.js hat auch eine aufgerufene Funktion getContractAddress
, kann aber nicht für Nonce verwendet werden)
import { ethers } from 'hardhat';
static getContractAddress(address: string, nonce: number): string {
const rlp_encoded = ethers.utils.RLP.encode(
[address, ethers.BigNumber.from(nonce.toString()).toHexString()]
);
const contract_address_long = ethers.utils.keccak256(rlp_encoded);
const contract_address = '0x'.concat(contract_address_long.substring(26));
return ethers.utils.getAddress(contract_address);
}
const contractAddress = ethers.utils.getContractAddress({from, nonce});
Die Vertragsadresse ist typischerweise ein Hash aus der Adresse des Absenders und der Brieftasche des Absenders. Der tatsächliche Vertragscode spielt keine Rolle - der Hash ist unabhängig vom Code derselbe.
Oben habe ich typisch gesagt, weil es andere Möglichkeiten gibt, Verträge einzusetzen. Wenn ein bestehender Vertrag einen Vertrag mit einem speziellen Opcode bereitstellt CREATE2
, wird die Vertragsadresse etwas anders berechnet.
Einzelheiten können Sie beispielsweise hier überprüfen: https://medium.com/coinmonks/smart-contract-address-creation-method-difference-between-smart-contract-address-and-wallet-97b421506455
Timofey Solonin