This docker compose will give you in one command line:
- Ethereum go-client (geth) running on port 8544 with
- 2 ethereum accounts, already unlocked
- Connected to Geneva devchain (network ID=2017042099)
- Mining the blocks (optional)
- Data are saved out of the container, on your host, so no problem if you delete the container
- Testrpc: eth-node running a test network on port 8545
- Truffle: where you can test and deploy smart contracts
- Netstatsapi: which will collect and send your node perf to our devchain dashboard on http://factory.shinit.net:15000
- Netstatsfront: dashboard to display eth stats (not used by default, please uncomment in
docker-compose.yml
if needed and browse http://localhost:3000 )
More info: you can find an overview of that setup on my blog: https://greg.satoshi.tech/
- A linux host, preferable ubuntu 14.x or 16.x. If you are on windows or MAC, please use docker toolbox or Vagrant --> see in annexes
- Docker v17+ and docker-compose v1.15+
- This code:
git clone https://github.com/gregbkr/geth-truffle-docker.git devchain && cd devchain
- Create an environment var to declare your geth node name:
echo "export GETH_NODE=<YOUR_NODE_NAME>" >> ~/.profile && source ~/.profile
- Check your node name:
echo $GETH_NODE
- Run the stack:
docker-compose up -d
- Check geth is up and answering locally:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' localhost:8544
- Check testrpc node is running:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' localhost:8545
- Other rpc commands here
- Check container logs:
docker logs geth
- Start shell in the geth container:
docker exec -it geth sh
- Interact with geth:
- List account:
geth --datadir=/root/.ethereum/devchain account list
(other geth commands here) - To see your keys:
cat /root/.ethereum/devchain/keystore/*
- Backup the account somewhere safe. For example, I saved this block for my wallet (carefull, you can steal my coins with these infos):
- List account:
{"address":"6e068b2fcf3ed73d5166d0b322fa10e784b7b4fe","crypto":{"cipher":"aes-128-ctr","ciphertext":"0d392da6deb66b13c95d1b723ea51a53ab58e1f7555c3a1263a5b203885b9e51","cipherparams":{"iv":"7a919e171cda132f375afd5f9e7c2ba1"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1f3f814262b9a4ce3c2f3e1cabb5788f0520101f00598aa0b84bbda08ceaaf31"},"mac":"8e8393e86fe2278666ec26e9956b49adc25bc2e7492d5a25ee30e8118dd17441"},"id":"71aa2bfd-ee91-4206-ab5e-82c38ccd071f","version":3}/
-
The account is on your host too:
- Exit docker
exit
- List docker volume:
docker volume ls
- See the physical location of your account:
docker volume inspect devchain_geth
- You can then browse the mount point
sudo ls /var/lib/docker/volumes/devchain_geth/_data/devchain
. From this location you can save or import another account (just copy/paste your key file)
- Exit docker
-
If needed, (within the geth container) create new account with:
- Create password:
echo "Geneva2017" > /root/.ethereum/devchain/pw2
- create account:
geth --datadir=/root/.ethereum/devchain account new --password /root/.ethereum/devchain/pw2
- Create password:
-
Mining (y/n)? In
docker-compose.yml
sectiongeth/command
add/remove--fast --mine
to the linecommand
and run againdocker-compose up -d
-
Use python to check your node, and later send ether:
- Install python requirements:
sudo apt-get install -y python3 python3-pip && pip3 install web3
- Check your node:
python3 scripts/checkWeb3.py
- Want to send ether? Edit
remote
,amountInEther
and commentexit()
, and run the same script
- Install python requirements:
-
Check that you can see your node name on our netstats dashboard: http://factory.shinit.net:15000
Truffle will compile, test, deploy your smart contract. In /dapp
folder, there are few examples of easy smart contracts. The addresses below are on the geneva devchain, feel free to play with it!
Definition of contracts running on devchain:
- HelloWorld: display a single message
- Contract addr:
0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55
- Contract name:
Greeter
- Functions:
Greet()
: display the recorded messageSetGreet()
: record a new message
- Commands:
Greeter.at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55').Greet()
Greeter.at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55').SetGreet("My new message!")
- Abi:
- Contract addr:
[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"Greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"SetGreet","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]
- MetaCoin: basic coin contract (default truffle contract you get when typing
truffle init
). Deployer's address gets 1000 coins- Contract addr:
0x718c8c6348b268d62c617cbd175703bd10b4f8fa
- Contract name:
MetaCoin
- Functions:
getBalance(addr)
: display balance in gwei -getBalanceInEth(addr)
: display balance in ethersendCoin(addr, amount)
: sent coin to address
- Command:
MetaCoin.at('0x718c8c6348b268d62c617cbd175703bd10b4f8fa').getBalance('0x99b77b612d43ba830d9db1eda0d0d23600db6874')
- Abi:
- Contract addr:
[{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"getBalanceInEth","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"sendCoin","outputs":[{"name":"sufficient","type":"bool"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"}]
- Counter: from 0, increment a simple counter, and see the result
- Contract addr:
0x44cd1f1fca0243f06f81238d039847855f3cf902
- Contract name:
Counter
- Functions:
increment()
: increment the counter each time you rungetCount()
: see the result
- Command:
Counter.at('0x44cd1f1fca0243f06f81238d039847855f3cf902').getCount()
- Abi:
- Contract addr:
[{"constant":true,"inputs":[],"name":"getCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[],"payable":false,"type":"function"}]
As there are less test examples of test in solidity, I choose to create only javascript test. These files ./test/*.js
will help you debug and strongly test your contract before to send it to production.
- Go in truffle container:
docker exec -it truffle sh
- Go to HelloWorld project:
cd /dapps/HelloWorld
- Check configuration:
cat truffle.js
<-- it should map withgeth:8544
andtestrpc:8545
- Check which value our contract will get at deployment :
cat migrations/2_deploy_contracts.js
, see:I am Groot!
- Test the contract against testrpc node:
truffle test --network testrpc
- If warning message:
authentication needed: password or unlock
--> you need to unlock your wallet (should be unlock when starting geth normally)truffle console --network devchain
web3.personal.unlockAccount(web3.personal.listAccounts[0], "17Fusion", 150000);
- Exit with
Crtl+D
While you can test and migrate your contract to testrpc, this environment only lives within your laptop. Next step is to deploy it to the devchain, so others will be able to access it.
- Send/migrate contract to devchain:
truffle migrate --network devchain
<-- you should get the contract address:Greeter: 0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55
(add--reset
if you want to redeploy an updated contract version) - Check your last deployment:
truffle network
- Access the console:
truffle console --network devchain
<-- Need to be in the right dapp folder to interact with contract - See last Greeter contract deployed:
Greeter.deployed()
<-- Greeter is the declared name of the contract - Greeter address:
Greeter.address
- Run the
Greet()
function (the main one) of our contract:Greeter.at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55').Greet()
- We can map our contract to an object:
var contract = Greeter.at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55')
- And simply call functions of this object:
contract.Greet()
For that you will need:
- The contract address:
0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55
- The abi: a description of the functions of our contract
- From our host: install jq:
sudo apt-get install jq
- And display the abi:
cat dapp/HelloWorld/build/contracts/Greeter.json | jq -c '.abi'
- Result:
- From our host: install jq:
[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"Greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"SetGreet","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]
- Go to a truffle's friend pc (or delete truffle container on your host
docker stop truffle; docker rm truffle
and start a new onedocker-compose up -d
), and interact with your contract: - Create the abi:
abi=[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"Greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"SetGreet","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]
- You can now run your contract function to see your custom message:
web3.eth.contract(abi).at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55').Greet()
- Launch a new contract (a clone) from the template Greeter known locally by truffle:
var greeter2 = Greeter.new("Hello gva")
- Get the address:
greeter2
- Check the output of this new address:
Greeter.at('0x5c57d316f698ff2dc3e2bf1b5f2117e8b88b4c55').Greet()
- List accounts:
web3.eth.accounts
- See account0 balance:
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[0]).toNumber(),'ether')
(could use.toNumber()
for better display) - Unlock your account:
web3.personal.unlockAccount(web3.personal.listAccounts[0], "17Fusion", 150000);
- Send some ether:
web3.eth.sendTransaction({from:web3.personal.accounts[0], to:'0x41df2990b4efd225f2bc12dd8b6455bf1c07ff6d', value: web3.toWei(10, "ether")})
- Send some ether between local account:
web3.eth.sendTransaction({from:web3.eth.accounts[0], to:web3.eth.accounts[1], value: web3.toWei(10, "ether") })
- Lottery: Bet some ether, and get a winner
- Contract addr:
Please deploy your own contract as it gets destroy when lottery finishes
- Contract name:
Lottery
- Functions:
bet()
: Send some ether with the transaction, in order to betGetBetInEther(address)
: return how much an address betGetUserAddress(int)
: return the address of the first/second better in the internal table/databasetestRandom()
: return the random number used in the lotteryendLottery()
: only the contract owner can stop the lottery which should reveal the winner (game is just an example as it is not really fair)
- Commands:
- Deploy contract and truffle console to it
- Create object lot:
lot=Lottery.at('your_contract_address')
- Return a simple var of the game:
lot.gameName()
- Bet from account0(bob):
lot.Bet({ from:web3.eth.accounts[0], value: web3.toWei(1, 'ether') })
- Bet from account1(alice):
lot.Bet({ from:web3.eth.accounts[1], value: web3.toWei(3, 'ether') })
- Check contract balance:
web3.fromWei(web3.eth.getBalance(lot.address).toNumber(),'ether')
- Check account0 balance:
web3.fromWei(web3.eth.getBalance(web3.eth.accounts[0]).toNumber(),'ether')
- Check total bet:
lot.totalBets().then( totalBets => console.log(totalBets.toNumber() ))
- Check bet of account0(bob):
lot.GetBetInEther.call(web3.eth.accounts[0]).then( bet => console.log( web3.fromWei(bet.toNumber(),'ether') ))
- Check address of first account:
lot.GetUserAddress.call('0').then( users => console.log( users ))
- Test the random number:
lot.test.call().then( num => console.log( num.toNumber() ))
- End the lottery (carefull here, contract's functions will be disabled after this last step):
lot.EndLottery({ from:web3.eth.accounts[0] }).then( winningNumber => console.log (winningNumber) )
--> Got an issue here returning the wining number
- Contract addr:
We will talk to our contract on devchain via a simple html page.
- Html page is located here:
./front/helloworld.html
- Make sure it target the right ethereum node:
HttpProvider("http://localhost:8544"))
for devchain - The script section of the page need the web3 library
web3.min.js
that you can obtain via:- Online mapping:
<script type="text/javascript" src="https://rawgit.com/ethereum/web3.js/develop/dist/web3.min.js">
- Or from
ethereum/web3.js
github:wget https://github.com/ethereum/web3.js/blob/develop/dist/web3.min.js
and link it with<script src="./web3.min.js"></script>
- Or node install:
cd ./front
,npm init
,npm install web3 --save
,npm install ethereum/web3.js --save
then browsels ./node_modules/web3/dist/web3.min.js
- Online mapping:
- Variable
abi
andcontract
should reference your contract abi and address - Open then the page
./front/helloworld.html
with your favorite browser.
To update Geth and truffle to the latest docker image, please rebuild the docker image, if a new image is present in dockerhub, it will get downloaded: docker-compose up -d --build
- Toolbox will create in the backgroud a linux virtual machine (with virtualbox), and gives you an invite so you can run docker commands.
- Install Docker toolbox for windows or MAC.
- Check docker and compose version:
docker version
docker-compose version
- For windows, we will need to run all commands from the Docker QuickStart shortcut (troubleshooting guide here )
- Install Git for windows or git for MAC
- Install the latest version of vagrant and virtualbox
- Clone our repo and go at the root
- Create vagrant vm:
vagrant up
, and wait the vm to build - Access the vm:
vagrant ssh
- Access the files:
cd /vagrant/
--> You are now in an ubuntu host, you can continue the tuto!
- Install docker:
wget https://get.docker.com/ -O script.sh
chmod +x script.sh
sudo ./script.sh
sudo usermod -aG docker ${USER}
- check docker version:
docker version
Replace 1.15.0 with latest version available on https://github.com/docker/compose/releases
sudo -i
curl -L https://github.com/docker/compose/releases/download/1.15.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
exit
check docker-compose version docker-compose version