Hier ist die Seite mit dem Beispiel https://solidity.readthedocs.io
Hier sind zwei Verträge, die zusammenarbeiten sollen (meine Fragen sind unten):
Hier ist, wie ich denke, dass es funktioniert. Bitte korrigieren Sie meine Schritte, während ich versuche, Sie hindurchzuführen.
Da es für eine Adresse einen Vertrag gibt, werden diese beiden Verträge an zwei verschiedenen Adressen bereitgestellt.
In der Konstruktorfunktion von OwnedToken wird der Vertragserstellervariable ein Vertrag zugewiesen, der sich unter der Adresse msg.sender befindet . Über diese Variable ist es dann möglich, auf Funktionen dieses Vertrages zuzugreifen. Es lässt mich denken, dass der Ersteller dieses Vertrags ein anderer Vertrag sein muss ( TokenCreator ) .
Dann schaue ich mir den Code des TokenCreator- Vertrags an, um herauszufinden, welche Funktion für die Erstellung des OwnedToken- Vertrags verantwortlich ist. Ich finde es in Zeile 43 .
Jetzt bin ich ratlos. Wie funktioniert new OwnedToken(name) ? Wie sieht der TokenCreator- Vertrag OwnedToken ?
Außerdem habe ich gerade einen Compilerfehler entdeckt:
Es ist so aufregend, sich durch all diese Dinge zu pflügen! Danke, du bist großartig!
EDIT: Einige Änderungen vorgenommen, und es macht jetzt mehr Sinn
Ersetzen Sie den Code, in dem Sie den Fehler gefunden haben, durch:
function changeName(bytes32 newName) {
// Only the creator can alter the name --
// the comparison is possible since contracts
// are implicitly convertible to addresses.
if (msg.sender == address(creator))
name = newName;
}
und es wird im Solidity Online Compiler und im solc
Compiler via kompiliert geth
.
Q1 Da es für eine Adresse einen Vertrag gibt, werden diese beiden Verträge an zwei verschiedenen Adressen bereitgestellt.
Sie können beide Verträge separat kompilieren und bereitstellen – dies würde einige Änderungen am Code erfordern. Sehen Sie sich die ClassicCheck contract
und die SafeConditionalHFTransfer
Verträge an, die sich auf ClassicCheck
die Adresse beziehen, in Wie man Ether bedingt an ein anderes Konto nach einer Hard-Fork sendet, um sich vor Replay-Angriffen zu schützen .
Aber dieses Beispiel wurde für Sie geschrieben, um sie zusammen zu kompilieren. Sie stellen TokenCreator
an eine Vertragsadresse bereit.
Wenn Sie aufrufen TokenCreator.createToken(...)
, wird ein neuer OwnedToken(...)
Vertrag erstellt und die Vertragsadresse zurückgegeben. Diese Adresse müssen Sie speichern, da der TokenCreator
Vertrag sie nicht speichert.
F2 In der Konstruktorfunktion der OwnedToken -Vertragserstellervariable wird ein Vertrag zugewiesen, der sich unter der Adresse msg.sender befindet. Über diese Variable ist es dann möglich, auf Funktionen dieses Vertrages zuzugreifen. Es lässt mich denken, dass der Ersteller dieses Vertrags ein anderer Vertrag sein muss (TokenCreator)
Ja.
F3 Ich schaue mir dann den Code des TokenCreator-Vertrags an, um herauszufinden, welche Funktion für die Erstellung des OwnedToken-Vertrags verantwortlich ist. Ich finde es in Zeile 43.
Und es ist #50, das den OwnedToken
Vertrag erstellt.
Q4 Jetzt bin ich ratlos. Wie funktioniert new OwnedToken(name)? Wie sieht der TokenCreator-Vertrag OwnedToken?
new OwnedToken(name)
erstellt den neuen OwnedToken
Vertrag. Die createToken(...)
Funktion gibt dann die Adresse zurück, an OwnedToken(...)
der sie bereitgestellt wurde.
Notiz
Es gibt ein Problem mit dem Beispielcode, da nicht konstante Funktionen nicht ohne weiteres Werte zurückgeben können. Sie müssen ein Ereignis generieren, um die Token-Erstellungsadresse anzuzeigen. Siehe So erhalten Sie Werte, die von nicht konstanten Transaktionsfunktionen zurückgegeben werden? für mehr Details.
Oder Sie können den Vertrag ändern TokenCreator
, um die erstellte Vertragsadresse zu speichern, wie in How to get return values when function with argument is called? wo das Ergebnis gespeichert und von der answer.getNumResult("idOne")
konstanten Funktion aufgerufen wurde.
TokenCreator
speichert den Rückgabewert des createToken(...)
Aufrufs nicht und weiß daher nicht, wo OwnedToken
es bereitgestellt wurde.
Wenn Sie anrufen changeName(...)
, müssen Sie die Adresse angeben, an der Sie OwnedToken
bereitgestellt wurden.
TokenCreator
für einen TestlaufHier ist ein geänderter TokenCreator
Vertrag gespeichert in TokenCreator.sol
:
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
// It is fine to reference it as long as it is not used
// to create a new contract.
TokenCreator public creator;
address public owner;
string public name;
// This is the constructor which registers the
// creator and the assigned name.
function OwnedToken(string _name) {
owner = msg.sender;
// We do an explicit type conversion from `address`
// to `TokenCreator` and assume that the type of
// the calling contract is TokenCreator, there is
// no real way to check that.
creator = TokenCreator(msg.sender);
name = _name;
}
function changeName(string newName) {
// Only the creator can alter the name --
// the comparison is possible since contracts
// are implicitly convertible to addresses.
if (msg.sender == address(creator))
name = newName;
}
function transfer(address newOwner) {
// Only the current owner can transfer the token.
if (msg.sender != owner)
return;
// We also want to ask the creator if the transfer
// is fine. Note that this calls a function of the
// contract defined below. If the call fails (e.g.
// due to out-of-gas), the execution here stops
// immediately.
if (creator.isTokenTransferOK(owner, newOwner))
owner = newOwner;
}
}
contract TokenCreator {
mapping(string => address) addresses;
function getAddress(string name) constant returns (address) {
return addresses[name];
}
function createToken(string name)
returns (OwnedToken tokenAddress)
{
// Create a new Token contract and return its address.
// From the JavaScript side, the return type is simply
// "address", as this is the closest type available in
// the ABI.
tokenAddress = new OwnedToken(name);
addresses[name] = tokenAddress;
}
function changeName(string oldName, string newName) {
// Again, the external type of "tokenAddress" is
// simply "address".
address tokenAddress = addresses[oldName];
delete addresses[oldName];
addresses[newName] = tokenAddress;
OwnedToken(tokenAddress).changeName(newName);
}
function isTokenTransferOK(
address currentOwner,
address newOwner
) returns (bool ok) {
// Check some arbitrary condition.
address tokenAddress = msg.sender;
return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
}
}
Mit dem stripCrLf
Skript in How to load Solidity source file into geth habe ich den Quellcode auf Folgendes reduziert:
user@Kumquat:~$ echo "var tokenCreatorSource='`stripCrLf TokenCreator.sol`'"
var tokenCreatorSource='contract OwnedToken { TokenCreator public creator; address public owner; string public name; function OwnedToken(string _name) { owner = msg.sender; creator = TokenCreator(msg.sender); name = _name; } function changeName(string newName) { if (msg.sender == address(creator)) name = newName; } function transfer(address newOwner) { if (msg.sender != owner) return; if (creator.isTokenTransferOK(owner, newOwner)) owner = newOwner; }}contract TokenCreator { mapping(string => address) addresses; function getAddress(string name) constant returns (address) { return addresses[name]; } function createToken(string name) returns (OwnedToken tokenAddress) { tokenAddress = new OwnedToken(name); addresses[name] = tokenAddress; } function changeName(string oldName, string newName) { address tokenAddress = addresses[oldName]; delete addresses[oldName]; addresses[newName] = tokenAddress; OwnedToken(tokenAddress).changeName(newName); } function isTokenTransferOK( address currentOwner, address newOwner ) returns (bool ok) { address tokenAddress = msg.sender; return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); }}'
Ich betreibe ein Entwicklungsnetzwerk mit dem folgenden Befehl (ich habe mein Passwort in passwordfile
):
geth --datadir ~/devdata --dev --mine --minerthreads 1 --unlock 0 --password ~/passwordfile console
Ich füge den abgeflachten Code in die geth
Befehlszeile ein:
> var tokenCreatorSource='contract OwnedToken { TokenCreator public creator; address public owner; string public name; function OwnedToken(string _name) { owner = msg.sender; creator = TokenCreator(msg.sender); name = _name; } function changeName(string newName) { if (msg.sender == address(creator)) name = newName; } function transfer(address newOwner) { if (msg.sender != owner) return; if (creator.isTokenTransferOK(owner, newOwner)) owner = newOwner; }}contract TokenCreator { mapping(string => address) addresses; function getAddress(string name) constant returns (address) { return addresses[name]; } function createToken(string name) returns (OwnedToken tokenAddress) { tokenAddress = new OwnedToken(name); addresses[name] = tokenAddress; } function changeName(string oldName, string newName) { address tokenAddress = addresses[oldName]; delete addresses[oldName]; addresses[newName] = tokenAddress; OwnedToken(tokenAddress).changeName(newName); } function isTokenTransferOK( address currentOwner, address newOwner ) returns (bool ok) { address tokenAddress = msg.sender; return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); }}'
Und kompilieren Sie den Code mit dem folgenden Befehl:
> var tokenCreatorCompiled = web3.eth.compile.solidity(tokenCreatorSource);
Version: 0.3.5-0/RelWithDebInfo-Linux/g++/Interpreter
path: /usr/bin/solc
undefined
Sie können die generierte ABI sehen:
> tokenCreatorCompiled.TokenCreator.info.abiDefinition
[{
constant: false,
inputs: [{
name: "name",
type: "string"
}],
name: "createToken",
outputs: [{
name: "tokenAddress",
type: "address"
}],
type: "function"
}, {
constant: false,
inputs: [{
name: "oldName",
type: "string"
}, {
name: "newName",
type: "string"
}],
name: "changeName",
outputs: [],
type: "function"
}, {
constant: true,
inputs: [{
name: "name",
type: "string"
}],
name: "getAddress",
outputs: [{
name: "",
type: "address"
}],
type: "function"
}, {
constant: false,
inputs: [{
name: "currentOwner",
type: "address"
}, {
name: "newOwner",
type: "address"
}],
name: "isTokenTransferOK",
outputs: [{
name: "ok",
type: "bool"
}],
type: "function"
}]
> tokenCreatorCompiled.OwnedToken.info.abiDefinition
[{
constant: true,
inputs: [],
name: "creator",
outputs: [{
name: "",
type: "address"
}],
type: "function"
}, {
constant: true,
inputs: [],
name: "name",
outputs: [{
name: "",
type: "string"
}],
type: "function"
}, {
constant: false,
inputs: [{
name: "newOwner",
type: "address"
}],
name: "transfer",
outputs: [],
type: "function"
}, {
constant: false,
inputs: [{
name: "newName",
type: "string"
}],
name: "changeName",
outputs: [],
type: "function"
}, {
constant: true,
inputs: [],
name: "owner",
outputs: [{
name: "",
type: "address"
}],
type: "function"
}, {
inputs: [{
name: "_name",
type: "string"
}],
type: "constructor"
}]
Erstellen Sie den Vertrag auf der Blockchain:
> var tokenCreatorContract = web3.eth.contract(tokenCreatorCompiled.TokenCreator.info.abiDefinition);
undefined
> var tokenCreator = tokenCreatorContract.new({
from:web3.eth.accounts[0],
data: tokenCreatorCompiled.TokenCreator.code, gas: 1000000},
function(e, contract) {
if (!e) {
if (!contract.address) {
console.log("Contract transaction send: TransactionHash: " +
contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
}
})
...
Contract mined! Address: 0xfdb30244a9d9b8e98e9dd57ac81728725830f54b
Erstellen Sie die BokkyPooBahToken
mit dem folgenden Befehl:
> tokenCreator.createToken("BokkyPooBahToken", {from:eth.accounts[0]})
"0xc55cc14b02bd0ca783dc7457bd5f09263fb5750135e7f7e4e2c070f6dd855785"
Sehen wir uns die Adresse des erstellten Tokens an:
> var tokenAddress = tokenCreator.getAddress("BokkyPooBahToken")
> tokenAddress
"0xcd347e1c299b02bba605f74e06031e7d170a1aed"
Sehen wir uns die Details des erstellten Tokens an:
> var token = eth.contract(tokenCreatorCompiled.OwnedToken.info.abiDefinition).at(tokenAddress);
undefined
> token.owner()
"0xfdb30244a9d9b8e98e9dd57ac81728725830f54b"
// Above is the TokenCreator contract address
> token.name()
"BokkyPooBahToken"
Lassen Sie uns den Namen des Tokens ändern:
> tokenCreator.changeName("BokkyPooBahToken", "BoppyKooBahToken", {from: eth.accounts[0]})
"0xa68045d36f313bad2abed292e86729ac9cf94c12a368b4d73a78f4a2826edf32"
> token.name()
"BoppyKooBahToken"
manidos
manidos
Datenschutz ist ein Menschenrecht.eth
TokenCreator.createToken(...)
. Dieses Konto ist das einzige autorisierte Konto, um anzurufenchangeName(...)
undtransfer(...)
. Nach der Übertragung ist dasOwnedToken
Eigentum des Kontos, auf das der ursprüngliche Eigentümer übertragen hat. Entschuldigen Sie sich nicht – ich habe einige Zeit mit Solidity und der Ethereum-Blockchain verbracht und lerne jeden Tag mehr dazu. Dieses Beispiel ist etwas kompliziert, zumal man den Rückgabewert nicht so einfach aus bekommen kannTokenCreator.createToken(...)
.Datenschutz ist ein Menschenrecht.eth
manidos
Datenschutz ist ein Menschenrecht.eth
@EugeneEpifanov
, habe ich ein Schritt-für-Schritt-Beispiel für die Bereitstellung und Ausführung der Funktionen eines modifizierten TokenCreator-Vertrags hinzugefügt. Die Modifikationen dienen dazu, die erstellte Token-Adresse für eine spätere Verwendung zu speichern.manidos
manidos