Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement evm RPC into test framework #1926

Merged
merged 14 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/ain-grpc/src/gen/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1260,3 +1260,4 @@ pub struct EthSyncingResult {
#[prost(message, optional, tag = "2")]
pub sync_info: ::core::option::Option<EthSyncingInfo>,
}

6 changes: 4 additions & 2 deletions lib/ain-grpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod codegen;
mod impls;
pub mod rpc;

use env_logger::{Builder as LogBuilder, Env};
use env_logger::{Builder as LogBuilder, Env, Target};
use jsonrpsee::core::server::rpc_module::Methods;
use jsonrpsee::http_server::HttpServerBuilder;
use log::Level;
Expand Down Expand Up @@ -52,7 +52,9 @@ pub fn add_grpc_server(_runtime: &Runtime, _addr: &str) -> Result<(), Box<dyn Er

pub fn init_runtime() {
log::info!("Starting gRPC and JSON RPC servers");
LogBuilder::from_env(Env::default().default_filter_or(Level::Info.as_str())).init();
LogBuilder::from_env(Env::default().default_filter_or(Level::Info.as_str()))
.target(Target::Stdout)
.init();
let _ = &*RUNTIME;
}

Expand Down
2 changes: 2 additions & 0 deletions make.sh
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,10 @@ test_py() {

_ensure_enter_dir "${release_target_dir}"

_fold_start "functional-tests"
prasannavl marked this conversation as resolved.
Show resolved Hide resolved
# shellcheck disable=SC2086
./test/functional/test_runner.py --tmpdirprefix "./test_runner/" --ansi "$@"
_fold_end
prasannavl marked this conversation as resolved.
Show resolved Hide resolved

_exit_dir
}
Expand Down
10 changes: 5 additions & 5 deletions src/chainparamsbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ const CBaseChainParams& BaseParams()
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN) {
return std::make_unique<CBaseChainParams>("", 8554, 8559);
return std::make_unique<CBaseChainParams>("", 8554, 8550, 8551);
} else if (chain == CBaseChainParams::TESTNET) {
return std::make_unique<CBaseChainParams>("testnet3", 18554, 9558);
return std::make_unique<CBaseChainParams>("testnet3", 18554, 18550, 18551);
} else if (chain == CBaseChainParams::DEVNET) {
if (gArgs.IsArgSet("-devnet-bootstrap")) {
return std::make_unique<CBaseChainParams>("devnet", 18554, 9558);
return std::make_unique<CBaseChainParams>("devnet", 18554, 18550, 18551);
} else {
return std::make_unique<CBaseChainParams>("devnet", 20554, 10558);
return std::make_unique<CBaseChainParams>("devnet", 20554, 20550, 20551);
}
} else if (chain == CBaseChainParams::REGTEST) {
return std::make_unique<CBaseChainParams>("regtest", 19554, 11558);
return std::make_unique<CBaseChainParams>("regtest", 19554, 19550, 19551);
} else {
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
Expand Down
4 changes: 3 additions & 1 deletion src/chainparamsbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ class CBaseChainParams
const std::string& DataDir() const { return strDataDir; }
int RPCPort() const { return nRPCPort; }
int GRPCPort() const { return nGRPCPort; }
int ETHRPCPort() const { return nETHRPCPort; }

CBaseChainParams() = delete;
CBaseChainParams(const std::string& data_dir, int rpc_port, int grpc_port) : nRPCPort(rpc_port), nGRPCPort(grpc_port), strDataDir(data_dir) {}
CBaseChainParams(const std::string& data_dir, int rpc_port, int grpc_port, int ethrpc_port) : nRPCPort(rpc_port), nGRPCPort(grpc_port), nETHRPCPort(ethrpc_port), strDataDir(data_dir) {}

private:
int nRPCPort;
int nGRPCPort;
int nETHRPCPort;
std::string strDataDir;
};

Expand Down
3 changes: 2 additions & 1 deletion src/defid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ static bool AppInit(int argc, char* argv[])
// Start GRPC after BaseParams() has been initialised
init_runtime();
int grpc_port = gArgs.GetArg("-grpcport", BaseParams().GRPCPort());
start_servers("127.0.0.1:" + std::to_string(grpc_port), "127.0.0.1:" + std::to_string(grpc_port + 1));
int eth_rpc_port = gArgs.GetArg("-ethrpcport", BaseParams().ETHRPCPort());
start_servers("127.0.0.1:" + std::to_string(eth_rpc_port), "127.0.0.1:" + std::to_string(grpc_port));

// Error out when loose non-argument tokens are encountered on command line
for (int i = 1; i < argc; i++) {
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ void SetupServerArgs()
gArgs.AddArg("-maxaddrratepersecond=<n>", strprintf("Sets MAX_ADDR_RATE_PER_SECOND limit for ADDR messages(default: %f)", MAX_ADDR_RATE_PER_SECOND), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxaddrprocessingtokenbucket=<n>", strprintf("Sets MAX_ADDR_PROCESSING_TOKEN_BUCKET limit for ADDR messages(default: %d)", MAX_ADDR_PROCESSING_TOKEN_BUCKET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-grpcport=<port>", strprintf("Start GRPC connections on <port> and <port + 1> (default: %u, testnet: %u, devnet: %u, regtest: %u)", defaultBaseParams->GRPCPort(), testnetBaseParams->GRPCPort(), devnetBaseParams->GRPCPort(), regtestBaseParams->GRPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
gArgs.AddArg("-ethrpcport=<port>", strprintf("Listen for ETH-JASON-RPC connections on <port>> (default: %u, testnet: %u, devnet: %u, regtest: %u)", defaultBaseParams->ETHRPCPort(), testnetBaseParams->ETHRPCPort(), devnetBaseParams->ETHRPCPort(), regtestBaseParams->ETHRPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);

#if HAVE_DECL_DAEMON
gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
Expand Down
1 change: 0 additions & 1 deletion src/test/data/script_tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,6 @@
["1", "IF 0xbd ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xbe ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xbf ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xc0 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xc1 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xc2 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
["1", "IF 0xc3 ELSE 1 ENDIF", "P2SH,STRICTENC", "BAD_OPCODE"],
Expand Down
2 changes: 1 addition & 1 deletion test/functional/rpc_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_categories(self):
# command titles
titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')]

components = ['Accounts', 'Blockchain', 'Control', 'Generating', 'Icxorderbook', 'Loan', 'Masternodes',
components = ['Accounts', 'Blockchain', 'Control', 'Evm', 'Generating', 'Icxorderbook', 'Loan', 'Masternodes',
'Mining', 'Network', 'Oracles', 'Poolpair', 'Proposals', 'Rawtransactions', 'Spv', 'Stats',
'Tokens', 'Util', 'Vault']

Expand Down
5 changes: 3 additions & 2 deletions test/functional/test_framework/authproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,15 @@ def get_request(self, *args, **argsn):
if args and argsn:
raise ValueError('Cannot handle both named and positional arguments')
return {'version': '1.1',
'jsonrpc': '2.0',
'method': self._service_name,
'params': args or argsn,
'id': AuthServiceProxy.__id_count}

def __call__(self, *args, **argsn):
postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
if response['error'] is not None:
if 'error' in response and response['error'] is not None:
raise JSONRPCException(response['error'], status)
elif 'result' not in response:
raise JSONRPCException({
Expand Down Expand Up @@ -177,7 +178,7 @@ def _get_response(self):
'code': -342, 'message': 'missing HTTP response from server'})

content_type = http_response.getheader('Content-Type')
if content_type != 'application/json':
if 'application/json' not in content_type:
raise JSONRPCException(
{'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (
http_response.status, http_response.reason)},
Expand Down
13 changes: 8 additions & 5 deletions test/functional/test_framework/test_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,11 @@ def get_default_config_path():
current_file_path + "/../../../build/test/config.ini",
current_file_path + "/../../config.ini",
current_file_path + "/../../../build/x86_64-pc-linux-gnu/test/config.ini",
current_file_path + "/../../../build/x86_64-apple-darwin/test/config.ini",
current_file_path + "/../../../build/x86_64-w64-mingw32/test/config.ini",
current_file_path + "/../../../build/aarch64-linux-gnu/test/config.ini",
current_file_path + "/../../../build/arm-linux-gnueabihf/test/config.ini",
current_file_path + "/../../../build/aarch64-linux-gnu/test/config.ini",
current_file_path + "/../../../build/x86_64-w64-mingw32/test/config.ini",
current_file_path + "/../../../build/x86_64-apple-darwin/test/config.ini",
current_file_path + "/../../../build/aarch64-apple-darwin/test/config.ini",
]
for p in default_config_paths:
if os.path.exists(p):
Expand Down Expand Up @@ -478,7 +479,7 @@ def run_test(self):

# Public helper methods. These can be accessed by the subclass test scripts.

def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None):
def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, evm_rpchost=None, binary=None):
"""Instantiate TestNode objects.

Should only be called once after the nodes have been specified in
Expand All @@ -500,6 +501,7 @@ def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None):
get_datadir_path(self.options.tmpdir, i),
chain=self.chain,
rpchost=rpchost,
evm_rpchost=evm_rpchost,
timewait=self.rpc_timeout,
defid=binary[i],
defi_cli=self.options.deficli,
Expand Down Expand Up @@ -649,6 +651,7 @@ def _initialize_chain(self):
extra_conf=["bind=127.0.0.1"],
extra_args=[],
rpchost=None,
evm_rpchost=None,
timewait=self.rpc_timeout,
defid=self.options.defid,
defi_cli=self.options.deficli,
Expand Down Expand Up @@ -695,7 +698,7 @@ def cache_path(*paths):

for entry in os.listdir(cache_path()):
if entry not in ['chainstate', 'blocks', 'enhancedcs', 'anchors',
'history']: # Only keep chainstate and blocks folder
'history', 'evm']: # Only keep chainstate and blocks folder
os.remove(cache_path(entry))

for i in range(self.num_nodes):
Expand Down
81 changes: 78 additions & 3 deletions test/functional/test_framework/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
delete_cookie_file,
get_rpc_proxy,
rpc_url,
eth_rpc_url,
wait_until,
p2p_port,
)
Expand Down Expand Up @@ -58,7 +59,7 @@ class TestNode():
To make things easier for the test writer, any unrecognised messages will
be dispatched to the RPC connection."""

def __init__(self, i, datadir, *, chain, rpchost, timewait, defid, defi_cli, coverage_dir, cwd, extra_conf=None,
def __init__(self, i, datadir, *, chain, rpchost, evm_rpchost, timewait, defid, defi_cli, coverage_dir, cwd, extra_conf=None,
extra_args=None, use_cli=False, start_perf=False, use_valgrind=False):
"""
Kwargs:
Expand All @@ -73,6 +74,7 @@ def __init__(self, i, datadir, *, chain, rpchost, timewait, defid, defi_cli, cov
self.stderr_dir = os.path.join(self.datadir, "stderr")
self.chain = chain
self.rpchost = rpchost
self.evm_rpchost = evm_rpchost
self.rpc_timeout = timewait
self.binary = defid
self.coverage_dir = coverage_dir
Expand Down Expand Up @@ -118,7 +120,9 @@ def __init__(self, i, datadir, *, chain, rpchost, timewait, defid, defi_cli, cov
self.process = None
self.rpc_connected = False
self.rpc = None
self.evm_rpc = None
self.url = None
self.evm_url = None
self.log = logging.getLogger('TestFramework.node%d' % i)
self.cleanup_on_exit = True # Whether to kill the node when this object goes away
# Cache perf subprocesses here by their data output filename.
Expand Down Expand Up @@ -151,6 +155,61 @@ def __init__(self, i, datadir, *, chain, rpchost, timewait, defid, defi_cli, cov
]
Mocktime = None

EVM_CALLS = {
# block
"eth_getBlockByHash",
"eth_getBlockByNumber",
"eth_getBlockTransactionCountByHash",
"eth_getBlockTransactionCountByNumber",
" eth_getUncleCountByBlockHash",
"eth_getUncleCountByBlockNumber",
# client
"eth_chainId",
"eth_syncing",
"eth_coinbase",
"eth_accounts",
"eth_blockNumber",
# execute
"eth_call",
"eth_estimateGas",
"eth_createAccessList",
# free market
"eth_gasPrice",
"eth_maxPriorityFeePerGas",
"eth_feeHistory",
# filter
"eth_newFilter",
"eth_newBlockFilter",
"eth_newPendingTransactionFilter",
"eth_uninstallFilter",
"eth_getFilterChanges",
"eth_getFilterLogs",
"eth_getLogs",
# mining
"eth_mining",
"eth_hashrate",
"eth_getWork",
"eth_submitWork",
"eth_submitHashrate",
# sign
"eth_sign",
"eth_signTransaction",
# state
"eth_getBalance",
"eth_getStorageAt",
"eth_getTransactionCount",
"eth_getCode",
"eth_getProof",
# submit
"eth_sendTransaction",
"eth_sendRawTransaction",
# transaction
"eth_getTransactionByHash",
"eth_getTransactionByBlockHashAndIndex",
"eth_getTransactionByBlockNumberAndIndex",
"eth_getTransactionReceipt",
}

def get_genesis_keys(self):
"""Return a deterministic priv key in base58, that only depends on the node's index"""
assert self.index <= len(self.PRIV_KEYS)
Expand Down Expand Up @@ -209,8 +268,14 @@ def __getattr__(self, name):
if self.use_cli:
return getattr(self.cli, name)
else:
assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection")
return getattr(self.rpc, name)
assert self.rpc_connected, self._node_msg("Error: no RPC connection")

if name in self.EVM_CALLS:
assert self.evm_rpc is not None, self._node_msg("Error: no EVM-RPC connection")
return getattr(self.evm_rpc, name)
else:
assert self.rpc is not None, self._node_msg("Error: no RPC connection")
return getattr(self.rpc, name)

def start(self, extra_args=None, *, cwd=None, stdout=None, stderr=None, **kwargs):
"""Start the node."""
Expand Down Expand Up @@ -259,11 +324,20 @@ def wait_for_rpc_connection(self):
rpc.getblockcount()
# If the call to getblockcount() succeeds then the RPC connection is up
self.log.debug("RPC successfully started")

evm_rpc = get_rpc_proxy(eth_rpc_url(self.datadir, self.index, self.chain, self.evm_rpchost), self.index,
timeout=self.rpc_timeout, coveragedir=self.coverage_dir)
evm_rpc.eth_blockNumber()
# If the call to eth_blockNumber() succeeds then the evm-RPC connection is up
self.log.debug("EVM-RPC successfully started")

if self.use_cli:
return
self.rpc = rpc
self.evm_rpc = evm_rpc
self.rpc_connected = True
self.url = self.rpc.url
self.evm_url = self.evm_rpc.url
return
except IOError as e:
if e.errno != errno.ECONNREFUSED: # Port not yet open?
Expand Down Expand Up @@ -331,6 +405,7 @@ def is_node_stopped(self):
self.process = None
self.rpc_connected = False
self.rpc = None
self.evm_rpc = None
self.log.debug("Node stopped")
return True

Expand Down
35 changes: 35 additions & 0 deletions test/functional/test_framework/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,15 @@ def p2p_port(n):
def rpc_port(n):
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)


def grpc_port(n):
return PORT_MIN + PORT_RANGE + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)


def eth_rpc_port(n):
return PORT_MIN + PORT_RANGE + PORT_RANGE + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)


def rpc_url(datadir, i, chain, rpchost):
rpc_u, rpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
Expand All @@ -306,6 +312,34 @@ def rpc_url(datadir, i, chain, rpchost):
return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))


def grpc_url(datadir, i, chain, grpchost):
grpc_u, grpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
port = grpc_port(i*2)
if grpchost:
parts = grpchost.split(':')
if len(parts) == 2:
host, port = parts
else:
host = grpchost

return "http://%s:%s@%s:%d" % (grpc_u, grpc_p, host, int(port))


def eth_rpc_url(datadir, i, chain, ethrpchost):
ethrpc_u, ethrpc_p = get_auth_cookie(datadir, chain)
host = '127.0.0.1'
port = eth_rpc_port(i)
if ethrpchost:
parts = ethrpchost.split(':')
if len(parts) == 2:
host, port = parts
else:
host = ethrpchost

return "http://%s:%s@%s:%d" % (ethrpc_u, ethrpc_p, host, int(port))


# Node functions
################

Expand All @@ -319,6 +353,7 @@ def initialize_datadir(dirname, n, chain):
f.write("port=" + str(p2p_port(n)) + "\n")
f.write("rpcport=" + str(rpc_port(n)) + "\n")
f.write("grpcport=" + str(grpc_port(n * 2)) + "\n") # GRPC will use two ports
f.write("ethrpcport=" + str(eth_rpc_port(n)) + "\n")
f.write("server=1\n")
f.write("keypool=1\n")
f.write("discover=0\n")
Expand Down