References

https://blog.csdn.net/wo541075754/article/details/98847308

https://zhuanlan.zhihu.com/p/33865441

https://learnblockchain.cn/2019/04/08/genesis

https://web3py.readthedocs.io/en/stable/

Installation

I’d like to install Ethereum from source, because it’s more easy and I can get the latest version.

According to the README.md, you may need to install Go (version 1.14 or later) and a C compiler first.

1
2
3
git clone https://github.com/ethereum/go-ethereum.git
cd go-ethereum
make geth

The output will be in go-ethereum/build/bin/ by default.

If you want to install in a different way, please refer to this.

Build Private Chain by Geth

We can modify the configuration of our own chain so that it will be more easy for us to debug the smart contract deployed and to trace each transaction.

We need to create a new directory to be the main workspace, and we need to create genesis.json in this directory before building the first block.

genesis.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
//
"config": {
"chainId": 15,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0
},
"coinbase" : "0x0000000000000000000000000000000000000000", // The address of the account that gets the reward by default
"difficulty" : "0x1", // Difficulty of mining
"extraData" : "",
"gasLimit" : "0xffffffff",
"nonce" : "0x0000000000000042", // Random number
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", // Related to PoW
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", // The parent node hash value. For the first block, it should be 0
"timestamp" : "0x00", // UTC timestamp
"alloc": { } // When the blockchain is generated, these accounts are given certain assets in advance
}

Run the command below to initialize.

1
geth --datadir "./" init genesis.json

How to Play with it

First we should notice that if we run the command like:

1
geth --datadir "./" --nodiscover --rpc --rpcaddr 127.0.0.1 --rpcport 7050 --rpcapi "debug,eth,net,web3,miner,personal" --networkid 10 console 2>>eth_log.log

There will be error when we trying to unlock an account:

This is because in the new version of geth, to keep secure, the HTTP channel unlocking account is disabled by default. We can refer this issue from github.

If we just want to debug on our private chain, this may work (simply adding --allow-insecure-unlock):

1
geth --datadir "./" --nodiscover --rpc --rpcaddr 127.0.0.1 --rpcport 7050 --rpcapi "debug,eth,net,web3,miner,personal" --networkid 10 --allow-insecure-unlock  console 2>>eth_log.log

Send Transaction

We can try to create 2 accounts to make transaction.

1
2
3
4
5
6
7
8
> personal.newAccount("aaa")
"0x5d31e74f0e52fe3fd58bd8079f84bc601a3f6d89"

> personal.newAccount("bbb")
"0xd14a5c1ca1299d003284483b77b789e28a50741f"

> eth.accounts
["0x5d31e74f0e52fe3fd58bd8079f84bc601a3f6d89", "0xd14a5c1ca1299d003284483b77b789e28a50741f"]

Let’s presume that 0x5d31e74f0e52fe3fd58bd8079f84bc601a3f6d89 has some ethereum, so if he want to send his ethereum to another account, we should unlock first.

1
2
3
4
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x5d31e74f0e52fe3fd58bd8079f84bc601a3f6d89
Passphrase:
true

Now we can try to send trasaction.

1
2
3
4
5
6
7
> eth.getBalance(eth.accounts[0])
624999999999959999600
> eth.getBalance(eth.accounts[1])
40000400

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: 20000000})
"0xe8cba28afecd5d8516096ccce7e18ae186ceea9e3d40ae8411412c7dc34e9695"

Seems like it works! Let’s check their balance again:

1
2
3
4
> eth.getBalance(eth.accounts[0])
624999999999959999600
> eth.getBalance(eth.accounts[1])
40000400

It’s weird! Both balance don’t change at all!

Actually, the transaction do have been commited to the blockchain, but has not been handled:

1
2
3
4
5
> txpool.status
{
pending: 1,
queued: 0
}

To make the transaction been processed, it must be mined. So we start mining, and then wait for a block to be mined, then stop mining.

1
2
3
4
5
6
> miner.start(1)
null
> eth.getBalance(eth.accounts[0])
654999999999939999600
> eth.getBalance(eth.accounts[1])
60000400

Done!

Deploy a Contract

Sometimes we need to deploy a smart contract on our own chain in order to debug, and we need to check a certain transaction detail after the transacton is done. It’s convenient for us to use the APIs from web3py to do them all.

Installing web3py:

1
sudo pip3 install web3

In the entire Ethereum network, the service we start through geth can be called a node, and rpc (which we have already included in the starting command) protocol can be used to send RPC operations to the node, and to receive data from the nodes. Using web3, it can be simply like this:

1
2
3
4
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7050"))
print(w3.isConnected())

In this case only, we use http://127.0.0.1:7050 to connect to our node (just as the parameters --rpc --rpcaddr 127.0.0.1 --rpcport 7050 stated). And if the output is true, we have successfully connected to our node.

There are more RPC methods to connect, according to the circumstances, you can choose one the most suitable for yourself:

  • IPCProvider
  • HTTPProvider
  • Auto check

And how can we deploy the contract on the node? Some APIs from web3py also work. First you need to compile your contract on Remix to get its bytecode and ABI. Using command solc in console to get them is also OK.

For instance, If we want to call a function from a contract, the python code should be like this (The solidity source code is from BalsnCTF 2019 Bank):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from web3 import Web3
import json
from datetime import datetime

from web3.types import TxReceipt

w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:7050"))
w3.isConnected()

_bytecode = "0x60806040526001805534801561001457600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610b52806100646000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631ec612411461005c5780638da5cb5b1461009d578063ed0242cf146100f4575b600080fd5b61009b60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff1916906020019092919050505061013f565b005b3480156100a957600080fd5b506100b26101c6565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610129600480360381019080803573ffffffffffffffffffffffffffffffffffffffff191690602001909291905050506101eb565b6040518082815260200191505060405180910390f35b600060028381548110151561015057fe5b906000526020600020906002020190508060000160009054906101000a900460ff1615151561017e57600080fd5b6101a483838360000160019054906101000a900467ffffffffffffffff1663ffffffff16565b60018160000160006101000a81548160ff021916908315150217905550505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060008160000160006101000a81548160ff021916908315150217905550828160000160096101000a8154816bffffffffffffffffffffffff021916908374010000000000000000000000000000000000000000900402179055503481600101819055506000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102d8576104548160000160016101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550610336565b670de0b6b3a764000034101515156102ef57600080fd5b662386f26fc1000081600101600082825403925050819055506107b38160000160016101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055505b600281908060018154018082558091505090600182039060005260206000209060020201600090919290919091506000820160009054906101000a900460ff168160000160006101000a81548160ff0219169083151502179055506000820160019054906101000a900467ffffffffffffffff168160000160016101000a81548167ffffffffffffffff021916908367ffffffffffffffff1602179055506000820160099054906101000a900474010000000000000000000000000000000000000000028160000160096101000a8154816bffffffffffffffffffffffff0219169083740100000000000000000000000000000000000000009004021790555060018201548160010155505050600160028054905003915050919050565b8181600060028381548110151561046757fe5b906000526020600020906002020160000160099054906101000a9004740100000000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff191682604051808273ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff19168152600c01915050604051809103902073ffffffffffffffffffffffffffffffffffffffff19161415156106f557828160000181905550428160010181905550818160020160006101000a8154816bffffffffffffffffffffffff021916908374010000000000000000000000000000000000000000900402179055503281600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819080600181540180825580915050906001820390600052602060002090600302016000909192909190915060008201548160000155600182015481600101556002820160009054906101000a900474010000000000000000000000000000000000000000028160020160006101000a8154816bffffffffffffffffffffffff0219169083740100000000000000000000000000000000000000009004021790555060028201600c9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050506107ac565b6a52b7d2dcc80cd2e4000000341015151561070f57600080fd5b7f2d3bd82a572c860ef85a36e8d4873a9deed3f76b9fddbf13fbe4fe8a97c4a57933604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5050505050565b818160006002838154811015156107c657fe5b906000526020600020906002020160000160099054906101000a9004740100000000000000000000000000000000000000000273ffffffffffffffffffffffffffffffffffffffff191682604051808273ffffffffffffffffffffffffffffffffffffffff191673ffffffffffffffffffffffffffffffffffffffff19168152600c01915050604051809103902073ffffffffffffffffffffffffffffffffffffffff1916141515610a5457828160000181905550428160010181905550818160020160006101000a8154816bffffffffffffffffffffffff021916908374010000000000000000000000000000000000000000900402179055503281600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819080600181540180825580915050906001820390600052602060002090600302016000909192909190915060008201548160000155600182015481600101556002820160009054906101000a900474010000000000000000000000000000000000000000028160020160006101000a8154816bffffffffffffffffffffffff0219169083740100000000000000000000000000000000000000009004021790555060028201600c9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681600201600c6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505050610b1f565b3373ffffffffffffffffffffffffffffffffffffffff166108fc600287815481101515610a7d57fe5b9060005260206000209060020201600101549081150290604051600060405180830381858888f19350505050158015610aba573d6000803e3d6000fd5b507f52a051bca553b0a466ddf55904bf9b9a11c8234e4b8c33f40bf79749a8df067033604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a15b50505050505600a165627a7a72305820ae68ace0c29a463c9b37bcd9d83993367624020a5247116238bf07b4e209a5f10029"
_abi_json = '[{"constant": false,"inputs": [{"name": "idx","type": "uint256"},{"name": "pass","type": "bytes12"}],"name": "withdraw","outputs": [],"payable": true,"stateMutability": "payable","type": "function"},{"constant": true,"inputs": [],"name": "owner","outputs": [{"name": "","type": "address"}],"payable": false,"stateMutability": "view","type": "function"},{"constant": false,"inputs": [{"name": "hash","type": "bytes12"}],"name": "deposit","outputs": [{"name": "","type": "uint256"}],"payable": true,"stateMutability": "payable","type": "function"},{"inputs": [],"payable": false,"stateMutability": "nonpayable","type": "constructor"},{"anonymous": false,"inputs": [{"indexed": false,"name": "addr","type": "address"}],"name": "SendEther","type": "event"},{"anonymous": false,"inputs": [{"indexed": false,"name": "addr","type": "address"}],"name": "SendFlag","type": "event"}]'
_abi = json.loads(_abi_json)

w3.geth.personal.unlockAccount(w3.eth.accounts[0], 'aaa')
BalsnCTFContract = w3.eth.contract(abi=_abi, bytecode=_bytecode)
txHash = BalsnCTFContract.constructor().transact({'from': w3.eth.accounts[0], 'gas': 1000000}) # The 'from' key states the manager of the contract.
txReceipt = w3.eth.waitForTransactionReceipt(txHash)
print(txReceipt)

bc = w3.eth.contract(address=txReceipt.contractAddress, abi=_abi)
txHash = bc.functions.deposit(b'thisisahash').transact({'from': w3.eth.accounts[0], 'to':w3.eth.accounts[1], 'gas': 10000000, 'value': w3.toWei(97, 'ether')}) # There cannot be too much ether for the transaction, otherwise an error will be reported.
txReceipt = w3.eth.waitForTransactionReceipt(txHash)
print(txReceipt)

However if we try to run it, the program seems to be suspended. Remember what we talk about in the previous chapter? Only if there is at least one node minning, the transaction would be handled. So all we need is just to mine one block like this:

1
miner.start(1); admin.sleepBlocks(1); miner.stop();

At last the execution result will be like this:

We can see that after the execution of BalsnCTFContract.constructor(), a contractAddress will be returned for users to locate the contract, and further more, we can use the address to call a function from the contract without deploying the contract again.

to be continued…