Ethereum: create raw JSON-RPC requests with Python for deploying and transacting with a smart contract

Salanfe
Salanfe
Feb 14, 2018 · 4 min read

February 2018.

Goal: this post explores how to send JSON-RPC requests to a Geth node in order to create raw transactions. The goal is to understand and see what is going on in the background when using a high level library such as web3py or web3js.

Also I am not interested in handling errors and exceptions. If anything goes wrong it will fail loudly. This post is indented to be educational only. For production, consider using web3py.

Summary: we will deploy and interact (calling functions and reading public variables) with a smart contract on a private network using only HTTP requests. Transactions are signed offline and only then sent to a geth node for processing.

For this guide I am using a private Proof-of-Authority network. If you would like to create such a network I invite you to read my previous post. Otherwise using Ganache (previously called TestRPC) or any Ethereum network is totally fine. Therefore I am not gonna cover anything about setting up a network here as the focus is using python to send HTTP requests to a Geth node.

If you just wanna see the full code, jump at the last Chapter.

Prerequisite

  1. Having access to a Ethereum network over IPC or RPC (might it be public, private, or a simulator like Ganache).
  2. Having python 3 installed. Personally I like the Anaconda distribution.
  3. Install the latest version of web3py.

1. Sending a simple request to Geth

Let’s warm up by sending a very simple request to Geth. Like asking the network id. The first step is to read the doc. The method we need is called net_version and is described here.

My Geth node URL and Port are: http://localhost:8501 . If you’re using Ganache with default value, the URL is probably http://localhost:7545 .

I am using the Requests python library to make my HTTP requests. Enough talking.

import requests# create persistent HTTP connection
session = requests.Session()
# as defined in https://github.com/ethereum/wiki/wiki/JSON-RPC#net_version
method = 'net_version'
params = []
payload= {"jsonrpc":"2.0",
"method":method,
"params":params,
"id":1}
headers = {'Content-type': 'application/json'}response = session.post('http://localhost:8501', json=payload, headers=headers)
print('raw json response: {}'.format(response.json()))
print('network id: {}'.format(response.json()['result']))

prints:

raw json response: {'id': 1, 'jsonrpc': '2.0', 'result': '1515'}
network id: 1515

Goob job ! From there we are ready to deploy and transact with a contract. Better build on good foundations. 1515 is the network id of my private Blockchain as defined in the genesis file. Everything looks great. With Ganache you should get 5777 for the network id.

But before being able to sign and send a transaction, we need an address, a private key and some ether.

2. Creating a private-public key pair and getting some ether

The web3py (release 4) library will help us create a key pair.

import web3w3 = web3.Web3()myAccount = w3.eth.account.create('put some extra entropy here')
myAddress = myAccount.address
myPrivateKey = myAccount.privateKey
print('my address is : {}'.format(myAccount.address))
print('my private key is : {}'.format(myAccount.privateKey.hex()))

in my case, I get:

my address is    : 0xF464A67CA59606f0fFE159092FF2F474d69FD675
my private key is: 0x94cb9f766ef067eb229da85213439cf4cbbcd0dc97ede9479be5ee4b7a93b96f

Please NEVER SHARE YOUR PRIVATE KEY ! I do it here because it is a local private development network that I am destroying and reboot a few times a day. I am not using this key pair on any public network.

Now to get some ether to this address there are multiple ways:

1. A very simple way is to add this address in your genesis.json file and to start a new network. From my previous guide, here is my genesis file that includes the address (remove 0x ) we have just created.

{
"config": {
"chainId": 1515,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"byzantiumBlock": 4,
"clique": {
"period": 5,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x5a722c92",
"extraData": "0x000000000000000000000000000000000000000000000000000000000000000008a58f09194e403d02a1928a7bf78646cfc260b087366ef81db496edd0ea2055ca605e8686eec1e60000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x8000000",
"difficulty": "0x1",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"08a58f09194e403d02a1928a7bf78646cfc260b0": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
},
"87366ef81db496edd0ea2055ca605e8686eec1e6": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
},
"F464A67CA59606f0fFE159092FF2F474d69FD675": {
"balance": "0x200000000000000000000000000000000000000000000000000000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

2. If you have a mining node or ganache, open a Geth Javascript Console and create a transaction by hand

$ geth attach ipc:'http://localhost:8501' // 7545 for ganache
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9
coinbase: 0x87366ef81db496edd0ea2055ca605e8686eec1e6
at block: 1585 (Wed, 14 Feb 2018 11:46:04 CET)
modules: eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> eth.sendTransaction({'from':eth.coinbase, 'to':'0xF464A67CA59606f0fFE159092FF2F474d69FD675', 'value':1000000000000000000000})
"0xdbc86acbe3644ac2cdb68132bbeecda40733c10f07ca16d87a2e5001e50eec4c"
> exit

here I am sending 1000 ether from 0x87366... to my address 0xF464A... . 1 ether is 1e18 wei (1 followed by 18 zeros). The unit of the value field is wei.

3. On public testnets, use a faucet.

3 Deploy and Transact with a Smart Contract

Great, now that we have an address with some ether (to pay for the gas cost) we can create our transaction offline, sign it, and send it to a node with a raw JSON-RPC HTTP request.

We are gonna use the send_rawTransaction method that takes as inputs the signed parameters of a transaction.

The python code is looking for the json file containing the contract abi and bytecode that truffle creates when compiling a smart contract. Before testing the python code, create a truffle workspace and compile the dummy contract AdditionContract.sol.

$ truffle init
// add the smart contract in contracts/
$ truffle compile

then update the python code with the URL of your geth node, and the path to the truffle workspace and your genesis file (don’t forget to replace my userName with yours in the path).

Everything else is in the code and should be self-explanatory.

I kept everything dead simple to ease modification and experimentation. Have fun :)

HackerNoon.com

how hackers start their afternoons.

Salanfe

Written by

Salanfe

Ethereum Developer. Mountaineering lover. Learning to talk to the lions

HackerNoon.com

how hackers start their afternoons.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade