本文探討了如何將JSON-RPC請求發送到Geth節點以建立原生的交易。目標是在使用高級庫(如web3py或web3js)時瞭解並查看後臺發生的狀況。php
另外,對處理錯誤和異常不是本文的重點。若是出現任何問題,它將只是顯示失敗。這篇文章主要是學習。對於生產環境,仍是考慮使用web3.py。html
咱們將僅使用HTTP請求在私有鏈上使用智能合約部署和交互(調用函數和讀取公共變量)。交易是離線簽名的,而後才發送到geth節點進行處理。java
對於本指南,咱們使用的是私有的Proof-of-Authority網絡。若是想建立這樣一個網絡,能夠閱讀咱們之前的帖子。本文假設使用Ganache(之前稱爲TestRPC)或任何以太坊網絡都徹底沒問題。所以,不會介紹有關在網絡設置的任何內容,重點是使用python將HTTP請求發送到Geth節點。node
讓咱們經過向Geth發送一個很是簡單的請求來熱個身。查詢下網絡ID。 第一步是閱讀文檔。 咱們須要的方法稱爲net_version
,在此處進行描述。python
個人Geth節點URL和端口是:http://localhost:8501
。若是你使用的是具備默認值的Ganache
,則URL多是http://localhost:7545
。linux
我正在使用Requests python library來發出個人HTTP請求。android
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']))
結果是:git
raw json response: {'id': 1, 'jsonrpc': '2.0', 'result': '1515'} network id: 1515
不錯,從那裏咱們準備好與合約一塊兒部署和交易,這創建了一個良好的基礎。1515是個人私有區塊鏈的網絡ID,如創世文件中所定義。目前看起來都很棒。 使用Ganache,應該得到5777的網絡ID。程序員
但在可以簽署和發送交易以前,咱們須要一個地址,一個私鑰和一些以太幣。github
web3py(release 4)庫將幫助咱們建立密鑰對。
import web3 w3 = 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()))
在這個示例中,我獲得:
my address is : 0xF464A67CA59606f0fFE159092FF2F474d69FD675 my private key is: 0x94cb9f766ef067eb229da85213439cf4cbbcd0dc97ede9479be5ee4b7a93b96f
請永遠不要分享你的私鑰!我這樣作是由於它是一個本地私有鏈,我天天都要銷燬並重啓幾回。我沒有在任何公共網絡上使用這個密鑰對。
如今爲了得到這個地址,有多種方法:
1.一種很是簡單的方法是在genesis.json
文件中添加此地址並啓動新網絡。下面是以前個人創世紀文件,其中包括咱們剛剛建立的地址(刪除0x
)。
{ "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.若是你有能夠挖礦的節點或ganache
,請打開Geth Javascript控制檯並手動建立交易:
$ 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
這裏我從0x87366...
發送1000以太幣到個人地址0xF464A...
,1個以太坊是10的18次方wei(1個後跟18個零)。值的單位是wei。
3.在公共測試鏈上,使用faucet。
太好了,既然咱們有一個帶有一些以太網的地址(爲了支付gas費用),咱們能夠離線建立咱們的交易,簽名並將其發送到具備原生JSON-RPC
的HTTP請求節點。
咱們將使用send_rawTransaction
方法,該方法將交易的簽名做爲輸入參數。
python代碼正在查詢truffle
在編譯智能合約時建立的包含合約abi
和字節碼的json文件。在測試python代碼以前,建立一個truffle
工做區並編譯虛擬合約AdditionContract.sol
。
$ truffle init // add the smart contract in contracts/ $ truffle compile
而後更新python代碼,用geth
節點的URL以及truffle
工做空間和genesis
文件的路徑(不要忘記在路徑中用你的userName替換個人userName)。
其餘一切都在代碼中,應該是不言自明的。
pragma solidity ^0.4.18; contract AdditionContract { uint public state = 0; function add(uint value1, uint value2) public { state = value1 + value2; } function getState() public constant returns (uint) { return state; } }
文末附完整代碼。
咱們讓一切都變得簡單易於修改和測試。 玩的開心 :)
python用web3.py庫開發以太坊來講很是的方便,有興趣的用戶能夠關注咱們的python以太坊教程,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
另外其餘語言能夠學習的以太坊教程以下:
匯智網原創翻譯,轉載請標明出處。這裏是原文
raw_JSON_RPC_requests_to_smart_contract.py
# associated medium post: https://medium.com/@ethervolution/ethereum-create-raw-json-rpc-requests-with-python-for-deploying-and-transacting-with-a-smart-7ceafd6790d9 import requests import json import web3 # Release 4.0.0-beta.8 import pprint import time # create persistent HTTP connection session = requests.Session() w3 = web3.Web3() pp = pprint.PrettyPrinter(indent=2) requestId = 0 # is automatically incremented at each request URL = 'http://localhost:8501' # url of my geth node PATH_GENESIS = '/home/salanfe/privateNetworks/geth_PoA/genesis.json' PATH_SC_TRUFFLE = '/home/salanfe/Projects/AdditionContract/' # smart contract path # extracting data from the genesis file genesisFile = json.load(open(PATH_GENESIS)) CHAINID = genesisFile['config']['chainId'] PERIOD = genesisFile['config']['clique']['period'] GASLIMIT = int(genesisFile['gasLimit'],0) # compile your smart contract with truffle first truffleFile = json.load(open(PATH_SC_TRUFFLE + '/build/contracts/AdditionContract.json')) abi = truffleFile['abi'] bytecode = truffleFile['bytecode'] # Don't share your private key ! myAddress = '0xF464A67CA59606f0fFE159092FF2F474d69FD675' # address funded in genesis file myPrivateKey = '0x94cb9f766ef067eb229da85213439cf4cbbcd0dc97ede9479be5ee4b7a93b96f' ''' =========================== SOME FUNCTIONS ============================ ''' # see http://www.jsonrpc.org/specification # and https://github.com/ethereum/wiki/wiki/JSON-RPC def createJSONRPCRequestObject(_method, _params, _requestId): return {"jsonrpc":"2.0", "method":_method, "params":_params, # must be an array [value1, value2, ..., valueN] "id":_requestId}, _requestId+1 def postJSONRPCRequestObject(_HTTPEnpoint, _jsonRPCRequestObject): response = session.post(_HTTPEnpoint, json=_jsonRPCRequestObject, headers={'Content-type': 'application/json'}) return response.json() ''' ======================= DEPLOY A SMART CONTRACT ======================= ''' ### get your nonce requestObject, requestId = createJSONRPCRequestObject('eth_getTransactionCount', [myAddress, 'latest'], requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) myNonce = w3.toInt(hexstr=responseObject['result']) print('nonce of address {} is {}'.format(myAddress, myNonce)) ### create your transaction transaction_dict = {'from':myAddress, 'to':'', # empty address for deploying a new contract 'chainId':CHAINID, 'gasPrice':1, # careful with gas price, gas price below the --gasprice option of Geth CLI will cause problems. I am running my node with --gasprice '1' 'gas':2000000, # rule of thumb / guess work 'nonce':myNonce, 'data':bytecode} # no constrctor in my smart contract so bytecode is enough ### sign the transaction signed_transaction_dict = w3.eth.account.signTransaction(transaction_dict, myPrivateKey) params = [signed_transaction_dict.rawTransaction.hex()] ### send the transacton to your node requestObject, requestId = createJSONRPCRequestObject('eth_sendRawTransaction', params, requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) transactionHash = responseObject['result'] print('contract submission hash {}'.format(transactionHash)) ### wait for the transaction to be mined and get the address of the new contract while(True): requestObject, requestId = createJSONRPCRequestObject('eth_getTransactionReceipt', [transactionHash], requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) receipt = responseObject['result'] if(receipt is not None): if(receipt['status'] == '0x1'): contractAddress = receipt['contractAddress'] print('newly deployed contract at address {}'.format(contractAddress)) else: pp.pprint(responseObject) raise ValueError('transacation status is "0x0", failed to deploy contract. Check gas, gasPrice first') break time.sleep(PERIOD/10) ''' ================= SEND A TRANSACTION TO SMART CONTRACT ================''' ### get your nonce requestObject, requestId = createJSONRPCRequestObject('eth_getTransactionCount', [myAddress, 'latest'], requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) myNonce = w3.toInt(hexstr=responseObject['result']) print('nonce of address {} is {}'.format(myAddress, myNonce)) ### prepare the data field of the transaction # function selector and argument encoding # https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding value1, value2 = 10, 32 # random numbers here function = 'add(uint256,uint256)' # from smart contract methodId = w3.sha3(text=function)[0:4].hex() param1 = (value1).to_bytes(32, byteorder='big').hex() param2 = (value2).to_bytes(32, byteorder='big').hex() data = '0x' + methodId + param1 + param2 transaction_dict = {'from':myAddress, 'to':contractAddress, 'chainId':CHAINID, 'gasPrice':1, # careful with gas price, gas price below the threshold defined in the node config will cause all sorts of issues (tx not bieng broadcasted for example) 'gas':2000000, # rule of thumb / guess work 'nonce':myNonce, 'data':data} ### sign the transaction signed_transaction_dict = w3.eth.account.signTransaction(transaction_dict, myPrivateKey) params = [signed_transaction_dict.rawTransaction.hex()] ### send the transacton to your node print('executing {} with value {},{}'.format(function, value1, value2)) requestObject, requestId = createJSONRPCRequestObject('eth_sendRawTransaction', params, requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) transactionHash = responseObject['result'] print('transaction hash {}'.format(transactionHash)) ### wait for the transaction to be mined while(True): requestObject, requestId = createJSONRPCRequestObject('eth_getTransactionReceipt', [transactionHash], requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) receipt = responseObject['result'] if(receipt is not None): if(receipt['status'] == '0x1'): print('transaction successfully mined') else: pp.pprint(responseObject) raise ValueError('transacation status is "0x0", failed to deploy contract. Check gas, gasPrice first') break time.sleep(PERIOD/10) ''' ============= READ YOUR SMART CONTRACT STATE USING GETTER ==============''' # we don't need a nonce since this does not create a transaction but only ask # our node to read it's local database ### prepare the data field of the transaction # function selector and argument encoding # https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding # state is declared as public in the smart contract. This creates a getter function methodId = w3.sha3(text='state()')[0:4].hex() data = '0x' + methodId transaction_dict = {'from':myAddress, 'to':contractAddress, 'chainId':CHAINID, 'data':data} params = [transaction_dict, 'latest'] requestObject, requestId = createJSONRPCRequestObject('eth_call', params, requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) state = w3.toInt(hexstr=responseObject['result']) print('using getter for public variables: result is {}'.format(state)) ''' ============= READ YOUR SMART CONTRACT STATE GET FUNCTIONS ==============''' # we don't need a nonce since this does not create a transaction but only ask # our node to read it's local database ### prepare the data field of the transaction # function selector and argument encoding # https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding # state is declared as public in the smart contract. This creates a getter function methodId = w3.sha3(text='getState()')[0:4].hex() data = '0x' + methodId transaction_dict = {'from':myAddress, 'to':contractAddress, 'chainId':CHAINID, 'data':data} params = [transaction_dict, 'latest'] requestObject, requestId = createJSONRPCRequestObject('eth_call', params, requestId) responseObject = postJSONRPCRequestObject(URL, requestObject) state = w3.toInt(hexstr=responseObject['result']) print('using getState() function: result is {}'.format(state)) ''' prints nonce of address 0xF464A67CA59606f0fFE159092FF2F474d69FD675 is 4 contract submission hash 0x64fc8ce5cbb5cf822674b88b52563e89f9e98132691a4d838ebe091604215b25 newly deployed contract at address 0x7e99eaa36bedba49a7f0ea4096ab2717b40d3787 nonce of address 0xF464A67CA59606f0fFE159092FF2F474d69FD675 is 5 executing add(uint256,uint256) with value 10,32 transaction hash 0xcbe3883db957cf3b643567c078081343c0cbd1fdd669320d9de9d05125168926 transaction successfully mined using getter for public variables: result is 42 using getState() function: result is 42 '''