-
Notifications
You must be signed in to change notification settings - Fork 7
Contracts and Transactions
THIS WIKI IS BEING EDITED AND REVIEWED NOW. PLEASE DO NOT RELY ON IT.
There are two types of accounts in Ethereum state. Normal or externally controlled accounts and contracts, i.e., sinppets of code, think a class. Both types of accounts have an ether balance.
The simplest transactions are then ether transfer transactions. But before we go into that just a quick recap about accounts:
Now in order to deploy the contract to the blockchain you need an account and some ether. You can list your accounts:
> eth.accounts
['0x036a03fc47084741f83938296a1c8ef67f6e34fa', '0xa8ade7feab1ece71446bed25fa0cf6745c19c3d5']The output is an array of addresses (ethereum addresses are 20byte long and written in hexadecimal form with the hex prefix 0x in front making it ... you guessed 42 byte long string). The first address is your primary account. Addresses are used to locate accounts that are in the ethereum state and it is crucial in identifying sender and recipient of a transaction. Also, an address is deterministically derived from a public key (the public part of an EC secp256k1 key pair).
But since i forgot the password, i just create a new one.
> admin.newAccount()
The new account will be encrypted with a passphrase.
Please enter a passphrase now.
Passphrase:
Repeat Passphrase:
'0xc7e9661b625f7ce5b7b959721ca6705d1291d102'
> eth.accounts
['0x036a03fc47084741f83938296a1c8ef67f6e34fa', '0xa8ade7feab1ece71446bed25fa0cf6745c19c3d5', '0xc7e9661b625f7ce5b7b959721ca6705d1291d102' ]If you want to use this account from now on for transactions, best to unlock it.
> admin.unlock(eth.accounts[2])
Please enter a passphrase now.
Passphrase:
trueRead more about how to manage your accounts.
If you are mining (and you wait long enough or get lucky), ether will appear on your account. This you can check with:
primaryAccount = eth.accounts[0]
web3.fromWei(eth.getBalance(primaryAccount), "ether")
//'666.00433'You can also set up wallet monitoring using watch and filters:
address = eth.accounts()[0];
eth.getBalance(address).toNumber();
eth.filter('pending').watch(function() {
print(eth.getBalance(address).toNumber());
});Assuming the account you are using as sender has sufficient funds, sending ether couldn't be easier. Which is also why you should probably be careful with this! You have been warned!
eth.sendTransaction({from: '0x036a03fc47084741f83938296a1c8ef67f6e34fa', to: '0xa8ade7feab1ece71446bed25fa0cf6745c19c3d5', value: web3.toWei(1, "ether")})For the frontier release, geth supports solidity compilation through system call to solc the command line solidity compiler by Christian R. and Lefteris K.
If you start up your geth node, you can check if this option is immediately available
eth.getCompilers()
['' ]
> eth.compile.solidity("")
error: eth_compileSolidity method not implemented
Invalid JSON RPC responseAfter you found a way to install solc, you make sure it's in the path, if eth.getCompilers() still does not find it (returns an empty array), you can set a custom path to the sol executable on the command line using th solc flag.
geth --datadir ~/frontier/00 --solc /usr/local/bin/solc --natspec
You can also set this option at runtime via the console:
> admin.setSolc("/usr/local/bin/solc")
solc v0.9.13
Solidity Compiler: /usr/local/bin/solc
Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015
trueLet us take this simple contract source:
> source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"This contract offers a unary method: called with a positive integer a, it returns a * 7.
Note that this document is not about writing interesting contracts or about the features of solidity.
For more information on contract language go through the [solidity tutorial], browse the contracts in our [dapp-bin], read up about [Dapp development], see solidity and dapp resources.
You are ready to compile solidity code in the geth JS console using eth.compile.solidity:
> contract = eth.compile.solidity(source)
{
code: '605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056',
info: {
language: 'Solidity',
languageVersion: '0',
compilerVersion: '0.9.13',
abiDefinition: [{
constant: false,
inputs: [{
name: 'a',
type: 'uint256'
} ],
name: 'multiply',
outputs: [{
name: 'd',
type: 'uint256'
} ],
type: 'function'
} ],
userDoc: {
methods: {
}
},
developerDoc: {
methods: {
}
},
source: 'contract test { function multiply(uint a) returns(uint d) { return a * 7; } }'
}
}The compiler is also available via RPC and therefore via web3.js to any in-browser Ðapp connecting to geth via RPC.
The following example shows how you interface geth via JSON-RPC to use the compiler.
./geth --datadir ~/eth/ --loglevel 6 --logtostderr=true --rpc --rpcport 8100 --rpccorsdomain '*' --mine console 2>> ~/eth/eth.log
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"],"id":1}' http://127.0.0.1:8100
The compiler output is combined into an object representing a single contract and is serialised as json. It contains the following fields:
-
code: the compiled EVM code -
source: the source code -
language: contract language (Solidity, Serpent, LLL) -
languageVersion: contract language version -
compilerVersion: compiler version -
abiDefinition: Application Binary Interface Definition -
userDoc: NatSpec user Doc -
developerDoc: NatSpec developer Doc
The immediate structuring of the compiler output (into code and info) reflects the two very different paths of deployment.
The compiled EVM code is sent off to the blockchain with a contract creation transaction while the rest (info) will ideally live on the decentralised cloud as publicly verifiable metadata complementing the code sitting on the blockchain.
Now that you got both an unlocked account as well as some funds, you can create a contract on the blockchain by sending a transaction to the empty address with the evm code as data. Simple ah?
primaryAddress = eth.accounts[0]
contractAddress = eth.sendTransaction({from: primaryAddress, data: evmCode})All binary data is serialised in hexadecimal form. Hex strings always have a hex prefix 0x.
Note that this step requires you to pay for execution and your balance on the account (that you put as sender in the from field) will be reduced according to the gas rules of the VM once your transaction makes it into a block. After some time, your transaction should appear included in a block confirming the state it brought about is a consensus. Your contract now lives on the blockchain.
The asynchronous way to do the same looks like this:
eth.sendTransaction({from: primaryAccount, data: evmCode}, function(err, address) {
if (!err)
console.log(address);
});So how did you pay for all this? Under the hood, the transaction specified a gas limit and a gasprice.
Gas limit is there to protect you from buggy code running until your funds are depleted. Gasprice represents the maximum amount of Wei that you are willing to pay for executing the transaction.
The gas expenditure incurred by running your contract will be bought by the ether you have in your account at a price you specified in the transaction with gasPrice. If you do not have the ether to cover all the gas requirements to complete running your code, the processing aborts and all intermediate state changes roll back to the pre-transaction snapshot. The gas used up to the point where execution stopped were used after all, so the ether balance of your account will reduce. These parameters can be adjusted on the transaction object fields gas, and gasPrice. The value field is used the same as in ether transfer transactions between normal accounts. In other words transferring funds is available between any two nodes, either normal or contract. If your contract runs out of funds, you should see an insufficient funds error. Note that any funds you send to a contract and not used to buy gas, will be returned to you when we release Homestead (see the rules of the game).
For testing and playing with contracts you can use the test network or set up a private node (or cluster) potentially isolated from the other nodes. If you then mine, you can make sure that your transaction will be included in the next block. You can see the pending transactions with:
eth.getBlock("pending", true).transactionsYou can retrieve blocks by number (height) or by their hash:
genesis = eth.getBlock(0)
eth.getBlock(genesis.hash).hash == genesis.hash
trueUse eth.blockNumber to get the current blockchain height and the "latest" magic parameter to access the current head (newest block).
currentHeight = eth.blockNumber()
eth.getBlock("latest").hash == eth.getBlock(eth.blockNumber).hash
trueIn the previous sections we explained how you create a contract on the blockchain. Now we deal with the rest of the compiler output, the contract metadata or contract info. The idea is that
- contract info is uploaded somewhere identifiable by a
urlwhich is publicly accessible - anyone can find out what the
urlis only knowing the contracts address
These requirements are achieved very simply by using a 2 step blockchain registry. The first step registers the contract code (hash) with a content hash in a contract called HashReg. The second step registers a url with the content hash in the UrlHint contract.
These simple registry contracts will be part of the frontier proposition.
By using this scheme, it is sufficient to know a contract's address to look up the url and fetch the actual contract metadata info bundle. Read on to learn why this is good.
So if you are a conscientious contract creator, the steps are the following:
- Get the contract info json file.
- Deploy contract info json file to any url of your choice
- Register codehash ->content hash -> url
- Deploy the contract itself to the blockchain
The JS API makes this process very easy by providing helpers. Call admin.contractInfo.register to extract info from the contract, write out its json serialisation in the given file, calculates the content hash of the file and finally registers this content hash to the contract's code hash.
Once you deployed that file to any url, you can use admin.contractInfo.registerUrl to register the url with your content hash on the blockchain as well. (Note that in case a fixed content addressed model is used as document store, the url-hint is no longer necessary.)
source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"
// compile with solc
contract = eth.compile.solidity(source)
// send off the contract to the blockchain
address = eth.sendTransaction({from: primaryAccount, data: contract.code})
// extracts info from contract, save the json serialisation in the given file,
// calculates the content hash and registers it with the code hash in `HashReg`
// it uses address to send the transaction.
// returns the content hash that we use to register a url
hash = admin.contractInfo.register(primaryAccount, address, contract, "~/dapps/shared/contracts/test/info.json")
// here you deploy ~/dapps/shared/contracts/test/info.json to a url
admin.contractInfo.registerUrl(primaryAccount, hash, url)eth.contract can be used to define a contract class that will comply with the contract interface as described in its ABI definition.
var Multiply7 = eth.contract(abiArray);
var multiply7 = new Multiply7();Now all the function calls specified in the abi are made available on the contract instance. You can just call those methods on the contract instance and chain sendTransaction({from: address}) or call() to it, to send it off to the ether.
multiply7.multiply.call(6)
42Now suppose this contract is not yours, and you would like documentation or look at the source code.
This is made possible by making available the contract info bundle and register it in the blockchain.
The admin.contractInfo API provides convenience methods to fetch this bundle for any contract that chose to register.
To see how it works, read about Contract Metadata or read the contract info deployment section of this document.
// get the contract info for contract address to do manual verification
var info = admin.contractInfo.get(address) // lookup, fetch, decode
var source = info.source;
var abiDef = info.abiDefinition// verify an existing contract in blockchain
admin.contractInfo.verify(address)This section will further elaborate what you can do with contracts and transactions building on a protocol NatSpec. Solidity implements smart comments doxigen style which then can be used to generate various facades meta documents of the code. One such use case is to generate custom messages for transaction confirmation that clients can prompt users with.
So we now extend the multiply7 contract with a smart comment specifying a custom confirmation message (notice).
contract test {
/// @notice Will multiply `a` by 7.
function multiply(uint a) returns(uint d) {
return a * 7;
}
}The comment has expressions in between backticks which are to be evaluated at the time the transaction confirmation message is presented to the user. The variables that refer to parameters of method calls then are instantiated in accordance with the actual transaction data sent by the user (or the user's dapp). NatSpec support for confirmation notices is fully implemented in geth. NatSpec relies on both the abi definition as well as the userDoc component to generate the proper confirmations. Therefore in order to access that, the contract needs to have registered its contract info as described above.
Let us see a full example. As a very conscientious smart contract dev, you first create your contract and deploy according to the recommended steps above:
source = "contract test {
/// @notice Will multiply `a` by 7.
function multiply(uint a) returns(uint d) {
return a * 7;
}
}"
contract = eth.compile.solidity(source)
contentHash = admin.contractInfo.register(contract, "~/dapps/shared/contracts/test/info.json")
// put it up on your favourite site:
admin.contractInfo.registerUrl(contentHash, "http://dapphub.com/test/info.json")For the purposes of a painless example just simply use the file url scheme (not exactly the cloud, but will show you how it works) without needing to deploy. admin.contractInfo.registerUrl(contentHash, "file:///home/nirname/dapps/shared/contracts/test/info.json").
Now you are done as a dev, so swap seats as it were and pretend that you are a user who is sending a transaction to the infamous multiply7 contract.
You need to start the client with the --natspec flag to enable smart confirmations and contractInfo fetching. You can also set it on the console with admin.contractInfo.start() and admin.contractInfo.stop().
geth --natspec --unlock primary console 2>> /tmp/eth.log
Now at the console type:
// obtain the abi definition for your contract
var info = admin.contractInfo.get(address)
var abiDef = info.abiDefinition
// instantiate a contract for transactions
var Multiply7 = eth.contract(abiDef);
var multiply7 = new Multiply7();And now try to send an actual transaction:
> multiply7.multiply.call(6)
NatSpec: Will multiply 6 by 7.
Would you like to proceed?
Confirm? [Y/N]
42
>primary = eth.accounts[0];
console.log("primary: "+primary);
balance = web3.fromWei(eth.getBalance(primary), "ether");
console.log("balance: "+balance);
admin.contractInfo.newRegistry(primary);
source = "contract test {\n" +
" /// @notice will multiply `a` by 7.\n" +
" function multiply(uint a) returns(uint d) {\n" +
" return a * 7;\n" +
" }\n" +
"} ";
contract = eth.compile.solidity(source);
contractaddress = eth.sendTransaction({from: primary, data: contract.code, gas: "1000000", gasPrice: "100000" });
eth.getBlock("pending", true).transactions;
console.log("multiply7 -> "+contractaddress);
admin.miner.start()
admin.debug.waitForBlocks(eth.blockNumer+4);
sleep(1)
admin.miner.stop()
code = eth.getCode(contractaddress);
console.log("code: "+code);
abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
Multiply7 = eth.contract(abiDef);
multiply7 = new Multiply7(contractaddress);
fortytwo = multiply7.multiply.call(6);
console.log("multiply7.multiply.call(6) => "+fortytwo);
admin.miner.start()
admin.debug.waitForBlocks(eth.blockNumer+4);
sleep(1)
admin.miner.stop()
filename = "/tmp/info.json";
contenthash = admin.contractInfo.register(primary, contractaddress, contract, filename);
admin.contractInfo.registerUrl(primary, contenthash, "file://"+filename);
admin.miner.start()
admin.debug.waitForBlocks(eth.blockNumer+4);
sleep(1)
admin.miner.stop()
info = admin.contractInfo.get(contractaddress);
console.log("Info: \n"+JSON.stringify(info));
var primary = eth.accounts[0];
var contractaddress = "0xa63e5373c52f06fb0847717912d67ec9dbbe3dec";
var abiDef = JSON.parse('[{"constant":false,"inputs":[{"name":"a","type":"uint256"}],"name":"multiply","outputs":[{"name":"d","type":"uint256"}],"type":"function"}]');
var Multiply7 = eth.contract(abiDef);
var multiply7 = new Multiply7(contractaddress);
var fortytwo = multiply7.multiply.sendTransaction(6, { from: primary, gas: "1000000", gasPrice: "100000" });
console.log("multiply7.multiply.call(6) => "+fortytwo);