mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			263 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| 	This file is part of cpp-ethereum.
 | |
| 
 | |
| 	cpp-ethereum is free software: you can redistribute it and/or modify
 | |
| 	it under the terms of the GNU General Public License as published by
 | |
| 	the Free Software Foundation, either version 3 of the License, or
 | |
| 	(at your option) any later version.
 | |
| 
 | |
| 	cpp-ethereum is distributed in the hope that it will be useful,
 | |
| 	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| 	GNU General Public License for more details.
 | |
| 
 | |
| 	You should have received a copy of the GNU General Public License
 | |
| 	along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| /** @file RPCSession.cpp
 | |
|  * @author Dimtiry Khokhlov <dimitry@ethdev.com>
 | |
|  * @date 2016
 | |
|  */
 | |
| 
 | |
| #include <string>
 | |
| #include <stdio.h>
 | |
| #include <thread>
 | |
| #include <libdevcore/CommonData.h>
 | |
| #include <json/reader.h>
 | |
| #include <json/writer.h>
 | |
| #include "RPCSession.h"
 | |
| 
 | |
| using namespace std;
 | |
| using namespace dev;
 | |
| 
 | |
| IPCSocket::IPCSocket(string const& _path): m_path(_path)
 | |
| {
 | |
| 	if (_path.length() >= sizeof(sockaddr_un::sun_path))
 | |
| 		BOOST_FAIL("Error opening IPC: socket path is too long!");
 | |
| 
 | |
| 	struct sockaddr_un saun;
 | |
| 	saun.sun_family = AF_UNIX;
 | |
| 	strcpy(saun.sun_path, _path.c_str());
 | |
| 
 | |
| 	if ((m_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
 | |
| 		BOOST_FAIL("Error creating IPC socket object");
 | |
| 
 | |
| 	int len = sizeof(saun.sun_family) + strlen(saun.sun_path);
 | |
| 
 | |
| 	if (connect(m_socket, reinterpret_cast<struct sockaddr const*>(&saun), len) < 0)
 | |
| 		BOOST_FAIL("Error connecting to IPC socket: " << _path);
 | |
| 
 | |
| 	m_fp = fdopen(m_socket, "r");
 | |
| }
 | |
| 
 | |
| string IPCSocket::sendRequest(string const& _req)
 | |
| {
 | |
| 	send(m_socket, _req.c_str(), _req.length(), 0);
 | |
| 
 | |
| 	char c;
 | |
| 	string response;
 | |
| 	while ((c = fgetc(m_fp)) != EOF)
 | |
| 	{
 | |
| 		if (c != '\n')
 | |
| 			response += c;
 | |
| 		else
 | |
| 			break;
 | |
| 	}
 | |
| 	return response;
 | |
| }
 | |
| 
 | |
| RPCSession& RPCSession::instance(const string& _path)
 | |
| {
 | |
| 	static RPCSession session(_path);
 | |
| 	BOOST_REQUIRE_EQUAL(session.m_ipcSocket.path(), _path);
 | |
| 	return session;
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_getCode(string const& _address, string const& _blockNumber)
 | |
| {
 | |
| 	return rpcCall("eth_getCode", { quote(_address), quote(_blockNumber) }).asString();
 | |
| }
 | |
| 
 | |
| RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string const& _transactionHash)
 | |
| {
 | |
| 	TransactionReceipt receipt;
 | |
| 	Json::Value const result = rpcCall("eth_getTransactionReceipt", { quote(_transactionHash) });
 | |
| 	BOOST_REQUIRE(!result.isNull());
 | |
| 	receipt.gasUsed = result["gasUsed"].asString();
 | |
| 	receipt.contractAddress = result["contractAddress"].asString();
 | |
| 	for (auto const& log: result["logs"])
 | |
| 	{
 | |
| 		LogEntry entry;
 | |
| 		entry.address = log["address"].asString();
 | |
| 		entry.data = log["data"].asString();
 | |
| 		for (auto const& topic: log["topics"])
 | |
| 			entry.topics.push_back(topic.asString());
 | |
| 		receipt.logEntries.push_back(entry);
 | |
| 	}
 | |
| 	return receipt;
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_sendTransaction(TransactionData const& _td)
 | |
| {
 | |
| 	return rpcCall("eth_sendTransaction", { _td.toJson() }).asString();
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_call(TransactionData const& _td, string const& _blockNumber)
 | |
| {
 | |
| 	return rpcCall("eth_call", { _td.toJson(), quote(_blockNumber) }).asString();
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_sendTransaction(string const& _transaction)
 | |
| {
 | |
| 	return rpcCall("eth_sendTransaction", { _transaction }).asString();
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_getBalance(string const& _address, string const& _blockNumber)
 | |
| {
 | |
| 	string address = (_address.length() == 20) ? "0x" + _address : _address;
 | |
| 	return rpcCall("eth_getBalance", { quote(address), quote(_blockNumber) }).asString();
 | |
| }
 | |
| 
 | |
| string RPCSession::eth_getStorageRoot(string const& _address, string const& _blockNumber)
 | |
| {
 | |
| 	string address = (_address.length() == 20) ? "0x" + _address : _address;
 | |
| 	return rpcCall("eth_getStorageRoot", { quote(address), quote(_blockNumber) }).asString();
 | |
| }
 | |
| 
 | |
| void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration)
 | |
| {
 | |
| 	rpcCall("personal_unlockAccount", { quote(_address), quote(_password), to_string(_duration) });
 | |
| }
 | |
| 
 | |
| string RPCSession::personal_newAccount(string const& _password)
 | |
| {
 | |
| 	return rpcCall("personal_newAccount", { quote(_password) }).asString();
 | |
| }
 | |
| 
 | |
| void RPCSession::test_setChainParams(vector<string> const& _accounts)
 | |
| {
 | |
| 	static std::string const c_configString = R"(
 | |
| 	{
 | |
| 		"sealEngine": "NoProof",
 | |
| 		"params": {
 | |
| 			"accountStartNonce": "0x",
 | |
| 			"maximumExtraDataSize": "0x1000000",
 | |
| 			"blockReward": "0x",
 | |
| 			"allowFutureBlocks": "1"
 | |
| 		},
 | |
| 		"genesis": {
 | |
| 			"author": "0000000000000010000000000000000000000000",
 | |
| 			"timestamp": "0x00",
 | |
| 			"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
 | |
| 			"extraData": "0x",
 | |
| 			"gasLimit": "0x1000000000000"
 | |
| 		},
 | |
| 		"accounts": {
 | |
| 			"0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } },
 | |
| 			"0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } },
 | |
| 			"0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } },
 | |
| 			"0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } }
 | |
| 		}
 | |
| 	}
 | |
| 	)";
 | |
| 
 | |
| 	Json::Value config;
 | |
| 	BOOST_REQUIRE(Json::Reader().parse(c_configString, config));
 | |
| 	for (auto const& account: _accounts)
 | |
| 		config["accounts"][account]["wei"] = "0x100000000000000000000000000000000000000000";
 | |
| 	test_setChainParams(Json::FastWriter().write(config));
 | |
| }
 | |
| 
 | |
| void RPCSession::test_setChainParams(string const& _config)
 | |
| {
 | |
| 	rpcCall("test_setChainParams", { _config });
 | |
| }
 | |
| 
 | |
| void RPCSession::test_rewindToBlock(size_t _blockNr)
 | |
| {
 | |
| 	rpcCall("test_rewindToBlock", { to_string(_blockNr) });
 | |
| }
 | |
| 
 | |
| void RPCSession::test_mineBlocks(int _number)
 | |
| {
 | |
| 	u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString()));
 | |
| 	rpcCall("test_mineBlocks", { to_string(_number) }, true);
 | |
| 
 | |
| 	//@TODO do not use polling - but that would probably need a change to the test client
 | |
| 	for (size_t polls = 0; polls < 100; ++polls)
 | |
| 	{
 | |
| 		if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number)
 | |
| 			return;
 | |
| 		std::this_thread::sleep_for(chrono::milliseconds(10)); //it does not work faster then 10 ms
 | |
| 	}
 | |
| 
 | |
| 	BOOST_FAIL("Error in test_mineBlocks: block mining timeout!");
 | |
| }
 | |
| 
 | |
| void RPCSession::test_modifyTimestamp(size_t _timestamp)
 | |
| {
 | |
| 	rpcCall("test_modifyTimestamp", { to_string(_timestamp) });
 | |
| }
 | |
| 
 | |
| Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& _args, bool _canFail)
 | |
| {
 | |
| 	string request = "{\"jsonrpc\":\"2.0\",\"method\":\"" + _methodName + "\",\"params\":[";
 | |
| 	for (size_t i = 0; i < _args.size(); ++i)
 | |
| 	{
 | |
| 		request += _args[i];
 | |
| 		if (i + 1 != _args.size())
 | |
| 			request += ", ";
 | |
| 	}
 | |
| 
 | |
| 	request += "],\"id\":" + to_string(m_rpcSequence) + "}";
 | |
| 	++m_rpcSequence;
 | |
| 
 | |
| 	//cout << "Request: " << request << endl;
 | |
| 	string reply = m_ipcSocket.sendRequest(request);
 | |
| 	//cout << "Reply: " << reply << endl;
 | |
| 
 | |
| 	Json::Value result;
 | |
| 	Json::Reader().parse(reply, result, false);
 | |
| 
 | |
| 	if (result.isMember("error"))
 | |
| 	{
 | |
| 		if (_canFail)
 | |
| 			return Json::Value();
 | |
| 
 | |
| 		BOOST_FAIL("Error on JSON-RPC call: " + result["error"]["message"].asString());
 | |
| 	}
 | |
| 	return result["result"];
 | |
| }
 | |
| 
 | |
| string const& RPCSession::accountCreateIfNotExists(size_t _id)
 | |
| {
 | |
| 	if (_id >= m_accounts.size())
 | |
| 	{
 | |
| 		m_accounts.push_back(personal_newAccount(""));
 | |
| 		personal_unlockAccount(m_accounts.back(), "", 100000);
 | |
| 	}
 | |
| 	return m_accounts[_id];
 | |
| }
 | |
| 
 | |
| RPCSession::RPCSession(const string& _path):
 | |
| 	m_ipcSocket(_path)
 | |
| {
 | |
| 	string account = personal_newAccount("");
 | |
| 	personal_unlockAccount(account, "", 100000);
 | |
| 	m_accounts.push_back(account);
 | |
| 	test_setChainParams(m_accounts);
 | |
| }
 | |
| 
 | |
| string RPCSession::TransactionData::toJson() const
 | |
| {
 | |
| 	Json::Value json;
 | |
| 	json["from"] = (from.length() == 20) ? "0x" + from : from;
 | |
| 	json["to"] = (to.length() == 20 || to == "") ? "0x" + to :  to;
 | |
| 	json["gas"] = gas;
 | |
| 	json["gasprice"] = gasPrice;
 | |
| 	json["value"] = value;
 | |
| 	json["data"] = data;
 | |
| 	return Json::FastWriter().write(json);
 | |
| 
 | |
| }
 |