From 34c77579603158b616ee8958707b3629157b7896 Mon Sep 17 00:00:00 2001 From: Peter John Bushnell Date: Fri, 9 Jun 2023 19:20:03 +0100 Subject: [PATCH] Prepare EVM Support Changes (#2037) * Port all CPP changes from feature/evm to master * Add file to check for args * lint: add local deps * test: add evm category * Fix typo * Add more CPP changes --------- Co-authored-by: Niven --- src/chainparams.cpp | 15 +- src/chainparamsseeds.h | 3 +- src/compat.h | 2 +- src/consensus/params.h | 2 + src/consensus/tx_check.cpp | 21 ++- src/consensus/tx_check.h | 1 + src/consensus/tx_verify.cpp | 6 +- src/defid.cpp | 5 +- src/fs.cpp | 1 + src/init.cpp | 61 +++++- src/key.cpp | 2 +- src/key_io.cpp | 17 +- src/masternodes/balances.h | 2 +- src/masternodes/ffi_temp_stub.h | 45 ++++- src/masternodes/govvariables/attributes.cpp | 2 +- src/masternodes/masternodes.cpp | 5 +- src/masternodes/masternodes.h | 3 +- src/masternodes/mn_checks.cpp | 36 ++-- src/masternodes/mn_rpc.h | 1 + src/masternodes/rpc_accounts.cpp | 65 +++++-- src/masternodes/rpc_customtx.cpp | 2 +- src/masternodes/rpc_loan.cpp | 4 + src/masternodes/rpc_poolpair.cpp | 15 ++ src/masternodes/rpc_proposals.cpp | 2 + src/masternodes/rpc_vault.cpp | 11 ++ src/masternodes/validation.cpp | 68 ++++++- src/masternodes/validation.h | 2 +- src/miner.cpp | 195 +++++++++++++++++--- src/miner.h | 4 +- src/net_processing.cpp | 3 +- src/policy/policy.cpp | 8 +- src/rpc/client.cpp | 4 + src/rpc/rawtransaction_util.cpp | 7 + src/rpc/rawtransaction_util.h | 1 + src/rpc/register.h | 4 +- src/script/signingprovider.cpp | 22 ++- src/script/signingprovider.h | 1 + src/script/standard.cpp | 2 +- src/spv/bitcoin/BRMerkleBlock.cpp | 2 +- src/spv/bitcoin/BRPeerManager.cpp | 2 +- src/spv/bitcoin/BRTransaction.cpp | 2 +- src/support/lockedpool.cpp | 2 +- src/test/scriptnum_tests.cpp | 2 +- src/txmempool.cpp | 32 ++-- src/validation.cpp | 182 ++++++++---------- src/validation.h | 2 +- src/wallet/wallet.cpp | 17 ++ src/wallet/wallet.h | 3 +- test/functional/rpc_help.py | 2 +- test/lint/check-rpc-mappings.py | 1 + test/lint/lint-locale-dependence.sh | 2 + 51 files changed, 673 insertions(+), 228 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c3415ae3b34..757009d7c3a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -226,6 +226,9 @@ class CMainParams : public CChainParams { consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + // EVM chain id + consensus.evmChainId = 1130; // ETH main chain ID + /** * The message start string is designed to be unlikely to occur in normal data. * The characters are rarely used upper ASCII, not valid as UTF-8, and produce @@ -499,6 +502,9 @@ class CTestNetParams : public CChainParams { consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + // EVM chain id + consensus.evmChainId = 1131; // test chain ID + pchMessageStartPostAMK[0] = pchMessageStart[0] = 0x0b; pchMessageStartPostAMK[1] = pchMessageStart[1] = 0x11; pchMessageStartPostAMK[2] = pchMessageStart[2] = 0x09; @@ -627,7 +633,7 @@ class CDevNetParams : public CChainParams { consensus.FortCanningEpilogueHeight = 1244000; consensus.GrandCentralHeight = 1366000; consensus.GrandCentralEpilogueHeight = 1438200; - consensus.NextNetworkUpgradeHeight = std::numeric_limits::max(); + consensus.NextNetworkUpgradeHeight = 1586750; consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 5 * 60; // 5 min == 10 blocks @@ -711,6 +717,9 @@ class CDevNetParams : public CChainParams { consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + // EVM chain id + consensus.evmChainId = 1132; // dev chain ID + pchMessageStartPostAMK[0] = pchMessageStart[0] = 0x0c; pchMessageStartPostAMK[1] = pchMessageStart[1] = 0x10; pchMessageStartPostAMK[2] = pchMessageStart[2] = 0x10; @@ -773,6 +782,7 @@ class CDevNetParams : public CChainParams { vSeeds.clear(); // nodes with support for servicebits filtering should be at the top vSeeds.emplace_back("35.187.53.161"); + vSeeds.emplace_back("34.89.47.54"); vFixedSeeds = std::vector(pnSeed6_devnet, pnSeed6_devnet + ARRAYLEN(pnSeed6_devnet)); fDefaultConsistencyChecks = false; @@ -926,6 +936,9 @@ class CRegTestParams : public CChainParams { consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + // EVM chain id + consensus.evmChainId = 1133; // regtest chain ID + pchMessageStartPostAMK[0] = pchMessageStart[0] = 0xfa; pchMessageStartPostAMK[1] = pchMessageStart[1] = 0xbf; pchMessageStartPostAMK[2] = pchMessageStart[2] = 0xb5; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 8d8852eb4df..acc80557899 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -29,6 +29,7 @@ static SeedSpec6 pnSeed6_test[] = { }; static SeedSpec6 pnSeed6_devnet[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xc5,0xa1,0x0e}, 20555} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xbb,0x35,0xa1}, 20555}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0x59,0x00,0x67}, 20555} }; #endif // DEFI_CHAINPARAMSSEEDS_H diff --git a/src/compat.h b/src/compat.h index ca5f3155b89..00fe996a7b1 100644 --- a/src/compat.h +++ b/src/compat.h @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #endif diff --git a/src/consensus/params.h b/src/consensus/params.h index bbb82af2b27..5b21801430f 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -223,6 +223,8 @@ struct Params { std::map nonUtxoBlockSubsidies; std::map newNonUTXOSubsidies; + + uint64_t evmChainId; }; } // namespace Consensus diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index 39716eb3a42..1cdd176a0af 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -38,7 +38,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe } // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock - if (fCheckDuplicateInputs) { + if (!IsEVMTx(tx) && fCheckDuplicateInputs) { std::set vInOutPoints; for (const auto& txin : tx.vin) { @@ -55,6 +55,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe if (tx.vin[0].scriptSig.size() < 2 || (tx.vin[0].scriptSig.size() > 100)) return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-length"); } + else if (IsEVMTx(tx)) + { + if (tx.vin[0].scriptSig.size() != 1 || tx.vin[1].scriptSig.size() != 1) + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-evm-length"); + } else { for (const auto& txin : tx.vin) @@ -132,3 +137,17 @@ bool IsTokenSplitTx(CTransaction const & tx, std::vector & metada } return result; } + +bool IsEVMTx(const CTransaction &tx) { + std::vector metadata; + auto hasAdditionalOpcodes{false}; + return (tx.vin.size() == 2 && + tx.vin[0].prevout.IsNull() && + tx.vin[1].prevout.IsNull() && + tx.vout.size() == 1 && + tx.vout[0].scriptPubKey.size() > 0 && + tx.vout[0].scriptPubKey[0] == OP_RETURN && + tx.vout[0].nValue == 0 && + ParseScriptByMarker(tx.vout[0].scriptPubKey, DfTxMarker, metadata, hasAdditionalOpcodes) && + metadata[0] == '9'); +} diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h index eac08d11edc..4c250781f9a 100644 --- a/src/consensus/tx_check.h +++ b/src/consensus/tx_check.h @@ -32,5 +32,6 @@ bool ParseScriptByMarker(CScript const & script, bool IsAnchorRewardTx(CTransaction const & tx, std::vector & metadata, bool fortCanning = false); bool IsAnchorRewardTxPlus(CTransaction const & tx, std::vector & metadata, bool fortCanning = false); bool IsTokenSplitTx(CTransaction const & tx, std::vector & metadata, bool fortCanningCrunch = true); +bool IsEVMTx(const CTransaction &tx); #endif // DEFI_CONSENSUS_TX_CHECK_H diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 3beafe4a541..bb02c5826b5 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -144,7 +144,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i { int64_t nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR; - if (tx.IsCoinBase()) + if (tx.IsCoinBase() || IsEVMTx(tx)) return nSigOps; if (flags & SCRIPT_VERIFY_P2SH) { @@ -163,6 +163,10 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, CCustomCSView& mnview, int nSpendHeight, CAmount& txfee, const CChainParams& chainparams) { + if (IsEVMTx(tx)) { + return true; + } + // are the actual inputs available? if (!inputs.HaveInputs(tx)) { return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", diff --git a/src/defid.cpp b/src/defid.cpp index 64a7da24ca9..0c7fa9c432c 100644 --- a/src/defid.cpp +++ b/src/defid.cpp @@ -60,6 +60,7 @@ static void WaitForShutdown() // static bool AppInit(int argc, char* argv[]) { + preinit(); InitInterfaces interfaces; interfaces.chain = interfaces::MakeChain(); @@ -163,6 +164,7 @@ static bool AppInit(int argc, char* argv[]) // If locking the data directory failed, exit immediately return false; } + init_evm_runtime(); fRet = AppInitMain(interfaces); } catch (const std::exception& e) { @@ -178,8 +180,7 @@ static bool AppInit(int argc, char* argv[]) WaitForShutdown(); } Shutdown(interfaces); - - stop_runtime(); + stop_evm_runtime(); return fRet; } diff --git a/src/fs.cpp b/src/fs.cpp index 7b422b8d701..9fa97bc271e 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -8,6 +8,7 @@ #endif #include #include +#include #endif namespace fsbridge { diff --git a/src/init.cpp b/src/init.cpp index 5ee8d691486..8f2da0af424 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -638,8 +638,10 @@ void SetupServerArgs() gArgs.AddArg("-dftxworkers=", strprintf("No. of parallel workers associated with the DfTx related work pool. Stock splits, parallel processing of the chain where appropriate, etc use this worker pool (default: %d)", DEFAULT_DFTX_WORKERS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-maxaddrratepersecond=", 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=", 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("-grpcbind=[:port]", "Bind to given address to listen for JSON-gRPC connections. Do not expose the gRPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -grpcport. This option can be specified multiple times (default: 127.0.0.1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); gArgs.AddArg("-grpcport=", strprintf("Start GRPC connections on and (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=", strprintf("Listen for ETH-JASON-RPC connections on > (default: %u, testnet: %u, devnet: %u, regtest: %u)", defaultBaseParams->ETHRPCPort(), testnetBaseParams->ETHRPCPort(), devnetBaseParams->ETHRPCPort(), regtestBaseParams->ETHRPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); + gArgs.AddArg("-ethrpcbind=[:port]", "Bind to given address to listen for ETH-JSON-RPC connections. Do not expose the ETH-RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -ethrpcport. This option can be specified multiple times (default: 127.0.0.1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); + gArgs.AddArg("-ethrpcport=", strprintf("Listen for ETH-JSON-RPC connections on > (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); @@ -1255,7 +1257,6 @@ bool AppInitParameterInteraction() } maxAddrRatePerSecond = gArgs.GetDoubleArg("-maxaddrratepersecond", Params().NetworkIDString() == CBaseChainParams::REGTEST ? MAX_ADDR_RATE_PER_SECOND_REGTEST : MAX_ADDR_RATE_PER_SECOND); - if (maxAddrRatePerSecond <= static_cast(0)) { return InitError("maxaddrratepersecond cannot be configured with a negative value."); } @@ -1554,10 +1555,53 @@ bool AppInitMain(InitInterfaces& interfaces) // ********************************************************* Step 4b: application initialization - init_runtime(); - int grpc_port = gArgs.GetArg("-grpcport", BaseParams().GRPCPort()); + /* Start the ETH RPC and gRPC servers. Current API only allows for one ETH + * RPC/gRPC server to bind to one address. By default, we will only take + * the first address, if multiple addresses are specified. + */ 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)); + int grpc_port = gArgs.GetArg("-grpcport", BaseParams().GRPCPort()); + std::vector > eth_endpoints; + std::vector > g_endpoints; + + // Determine which addresses to bind to ETH RPC server + if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-ethrpcbind"))) { // Default to loopback if not allowing external IPs + eth_endpoints.push_back(std::make_pair("127.0.0.1", eth_rpc_port)); + if (gArgs.IsArgSet("-rpcallowip")) { + LogPrintf("WARNING: option -rpcallowip was specified without -ethrpcbind; this doesn't usually make sense\n"); + } + if (gArgs.IsArgSet("-ethrpcbind")) { + LogPrintf("WARNING: option -ethrpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); + } + } else if (gArgs.IsArgSet("-ethrpcbind")) { // Specific bind address + for (const std::string& strETHRPCBind : gArgs.GetArgs("-ethrpcbind")) { + int port = eth_rpc_port; + std::string host; + SplitHostPort(strETHRPCBind, port, host); + eth_endpoints.push_back(std::make_pair(host, port)); + } + } + + // Determine which addresses to bind to gRPC server + if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-grpcbind"))) { // Default to loopback if not allowing external IPs + g_endpoints.push_back(std::make_pair("127.0.0.1", grpc_port)); + if (gArgs.IsArgSet("-rpcallowip")) { + LogPrintf("WARNING: option -rpcallowip was specified without -grpcbind; this doesn't usually make sense\n"); + } + if (gArgs.IsArgSet("-grpcbind")) { + LogPrintf("WARNING: option -grpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); + } + } else if (gArgs.IsArgSet("-grpcbind")) { // Specific bind address + for (const std::string& strGRPCBind : gArgs.GetArgs("-grpcbind")) { + int port = grpc_port; + std::string host; + SplitHostPort(strGRPCBind, port, host); + g_endpoints.push_back(std::make_pair(host, port)); + } + } + + // Default to using the first address passed to bind to ETH RPC server and gRPC server + start_servers(eth_endpoints[0].first + ":" + std::to_string(eth_endpoints[0].second), g_endpoints[0].first + "." + std::to_string(g_endpoints[0].second)); // ********************************************************* Step 5: verify wallet database integrity for (const auto& client : interfaces.chain_clients) { @@ -2218,9 +2262,8 @@ bool AppInitMain(InitInterfaces& interfaces) // Import privkey const auto key = DecodeSecret("L5DhrVPhA2FbJ1ezpN3JijHVnnH1sVcbdcAcp3nE373ooGH6LEz6"); - const auto pubkey = key.GetPubKey(); - const auto dest = WitnessV0KeyHash(PKHash{pubkey}); - const auto keyID = pubkey.GetID(); + const auto keyID = key.GetPubKey().GetID(); + const auto dest = WitnessV0KeyHash(PKHash{keyID}); const auto time{std::time(nullptr)}; auto pwallet = GetWallets()[0]; @@ -2289,7 +2332,7 @@ bool AppInitMain(InitInterfaces& interfaces) bool found = false; for (auto wallet : wallets) { - if (::IsMine(*wallet, destination)) { + if (::IsMine(*wallet, destination) & ISMINE_SPENDABLE) { found = true; break; } diff --git a/src/key.cpp b/src/key.cpp index 3e10e6d3f3b..ea9430172c0 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -269,7 +269,7 @@ bool CKey::Load(const CPrivKey &privkey, const CPubKey &vchPubKey, bool fSkipChe return VerifyPubKey(vchPubKey); } -#include + bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc, const bool ethAddress) const { assert(IsValid()); assert(IsCompressed()); diff --git a/src/key_io.cpp b/src/key_io.cpp index 8628d98ad99..2f98dd5a6c9 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -61,7 +61,22 @@ class DestinationEncoder std::string operator()(const WitnessV16EthHash& id) const { - return ETH_ADDR_PREFIX + HexStr(id); + // Raw addr = ETH_ADDR_PREFIX + HexStr(id); + // Produce ETH checksum address: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md + const auto address = HexStr(id); + std::vector input(address.begin(), address.end()); + std::vector output; + sha3(input, output); + const auto hashedAddress = HexStr(output); + std::string result; + for (size_t i{}; i < address.size(); ++i) { + if (std::isdigit(address[i]) || hashedAddress[i] < '8') { + result += address[i]; + } else { + result += std::toupper(address[i]); + } + } + return ETH_ADDR_PREFIX + result; } std::string operator()(const CNoDestination& no) const { return {}; } diff --git a/src/masternodes/balances.h b/src/masternodes/balances.h index 00014ef9ce4..2221e401580 100644 --- a/src/masternodes/balances.h +++ b/src/masternodes/balances.h @@ -208,7 +208,7 @@ struct CUtxosToAccountMessage { } }; -enum VMDomain : uint8_t { +enum class VMDomain : uint8_t { NONE = 0x00, // UTXO Reserved UTXO = 0x01, diff --git a/src/masternodes/ffi_temp_stub.h b/src/masternodes/ffi_temp_stub.h index 034da40030a..a980f33e393 100644 --- a/src/masternodes/ffi_temp_stub.h +++ b/src/masternodes/ffi_temp_stub.h @@ -26,6 +26,29 @@ struct CreateTransactionContext { using IsRelocatable = ::std::true_type; }; +struct FinalizeBlockResult final { + ::std::array<::std::uint8_t, 32> block_hash; + ::rust::Vec<::rust::Str> failed_transactions; + ::std::uint64_t miner_fee; + + using IsRelocatable = ::std::true_type; +}; + +struct RustRes final { + bool ok; + ::rust::Str reason; + + using IsRelocatable = ::std::true_type; +}; + +struct ValidateTxResult final { + ::std::uint64_t nonce; + ::std::array<::std::uint8_t, 20> sender; + ::std::uint64_t used_gas; + + using IsRelocatable = ::std::true_type; +}; + inline ::rust::Vec<::std::uint8_t> create_and_sign_tx(::CreateTransactionContext ctx) { return {}; } @@ -34,13 +57,13 @@ inline uint64_t evm_get_context() { return {}; } -inline ::rust::Vec<::std::uint8_t> evm_finalize(::std::uint64_t context, bool update_state, ::std::uint32_t difficulty, ::std::array<::std::uint8_t, 20> miner_address) { +inline FinalizeBlockResult evm_finalize(::std::uint64_t context, bool update_state, ::std::uint32_t difficulty, ::std::array<::std::uint8_t, 20> miner_address, ::std::uint64_t timestamp) { return {}; } inline void evm_discard_context(::std::uint64_t context) {} -inline uint64_t evm_get_balance(::rust::Str address) { +inline uint64_t evm_get_balance(::std::array<::std::uint8_t, 20> address) { return {}; } @@ -50,17 +73,27 @@ inline bool evm_sub_balance(::std::uint64_t context, ::rust::Str address, ::std: return {}; } -inline bool evm_validate_raw_tx(::rust::Str tx) { +inline ValidateTxResult evm_try_prevalidate_raw_tx(::RustRes &result, ::rust::Str tx) { + return {}; +} + +inline bool evm_try_queue_tx(::RustRes &result, ::std::uint64_t context, ::rust::Str raw_tx, ::std::array<::std::uint8_t, 32> native_tx_hash) { + return {}; +} + +inline bool evm_prevalidate_raw_tx(::rust::Str tx) { return {}; } -inline bool evm_queue_tx(::std::uint64_t context, ::rust::Str raw_tx) { +inline uint64_t evm_get_next_valid_nonce_in_context(uint64_t context, ::std::array<::std::uint8_t, 20> address) { return {}; } -inline void init_runtime() {} +inline void preinit() {} +inline void init_evm_runtime() {} inline void start_servers(::rust::Str json_addr, ::rust::Str grpc_addr) {} -inline void stop_runtime() {} +inline void stop_evm_runtime() {} +inline void evm_disconnect_latest_block() {}; #endif // DEFI_MASTERNODES_FFI_TEMP_STUB_H diff --git a/src/masternodes/govvariables/attributes.cpp b/src/masternodes/govvariables/attributes.cpp index 252668bc5b6..467f10a8c32 100644 --- a/src/masternodes/govvariables/attributes.cpp +++ b/src/masternodes/govvariables/attributes.cpp @@ -71,7 +71,7 @@ const std::map &ATTRIBUTES::displayTypes() { {AttributeTypes::Poolpairs, "poolpairs" }, {AttributeTypes::Token, "token" }, {AttributeTypes::Governance, "gov" }, - {AttributeTypes::Consortium, "consortium"} + {AttributeTypes::Consortium, "consortium"}, }; return types; } diff --git a/src/masternodes/masternodes.cpp b/src/masternodes/masternodes.cpp index 9dbab5007ef..af3cff16fac 100644 --- a/src/masternodes/masternodes.cpp +++ b/src/masternodes/masternodes.cpp @@ -49,6 +49,9 @@ int GetMnResignDelay(int height) { return Params().GetConsensus().mn.resignDelay; } + // Note: Getting new owner address for EVM miner reward passes + // max int to this function. If this delay is changed this will + // need to be updated. return Params().GetConsensus().mn.newResignDelay; } @@ -1051,7 +1054,7 @@ ResVal CCustomCSView::GetValidatedIntervalPrice(const CTokenCurrencyPai auto priceRecordIndex = useNextPrice ? 1 : 0; auto price = priceFeed.val->priceRecord[priceRecordIndex]; - + if (price <= 0) { return DeFiErrors::OracleNegativePrice(tokenSymbol, currency); } diff --git a/src/masternodes/masternodes.h b/src/masternodes/masternodes.h index 2ed258edf10..62a22962043 100644 --- a/src/masternodes/masternodes.h +++ b/src/masternodes/masternodes.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -476,7 +477,7 @@ class CCustomCSView : public CMasternodesView, LoanInterestV3ByVault, CVaultView :: VaultKey, OwnerVaultKey, CollateralKey, AuctionBatchKey, AuctionHeightKey, AuctionBidKey, CSettingsView :: KVSettings, - CProposalView :: ByType, ByCycle, ByMnVote, ByStatus + CProposalView :: ByType, ByCycle, ByMnVote, ByStatus >(); } // clang-format on diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index c976523fcb4..0b2b7c230b1 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -333,7 +333,7 @@ class CCustomMetadataParseVisitor { const Consensus::Params &consensus; const std::vector &metadata; - Res IsHardforkEnabled(int startHeight) const { + Res IsHardforkEnabled(const uint32_t startHeight) const { const std::unordered_map hardforks = { { consensus.AMKHeight, "called before AMK height" }, { consensus.BayfrontHeight, "called before Bayfront height" }, @@ -347,7 +347,7 @@ class CCustomMetadataParseVisitor { { consensus.GrandCentralHeight, "called before GrandCentral height" }, { consensus.NextNetworkUpgradeHeight, "called before NextNetworkUpgrade height" }, }; - if (startHeight && int(height) < startHeight) { + if (startHeight && height < startHeight) { auto it = hardforks.find(startHeight); assert(it != hardforks.end()); return Res::Err(it->second); @@ -3808,14 +3808,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { // Iterate over array of transfers for (const auto &[src, dst] : obj.transfers) { - if (src.domain == VMDomain::DVM) { + if (src.domain == static_cast(VMDomain::DVM)) { // Subtract balance from DFI address CBalances balance; balance.Add(src.amount); res = mnview.SubBalances(src.address, balance); if (!res) return res; - } else if (src.domain == VMDomain::EVM) { + } else if (src.domain == static_cast(VMDomain::EVM)) { // Subtract balance from ETH address CTxDestination dest; ExtractDestination(src.address, dest); @@ -3826,14 +3826,14 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { return DeFiErrors::TransferDomainNotEnoughBalance(EncodeDestination(dest)); } } - if (dst.domain == VMDomain::DVM) { + if (dst.domain == static_cast(VMDomain::DVM)) { // Add balance to DFI address CBalances balance; balance.Add(dst.amount); res = mnview.AddBalances(dst.address, balance); if (!res) return res; - } else if (dst.domain == VMDomain::EVM) { + } else if (dst.domain == static_cast(VMDomain::EVM)) { // Add balance to ETH address CTxDestination dest; ExtractDestination(dst.address, dest); @@ -3855,12 +3855,18 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor { if (obj.evmTx.size() > static_cast(EVM_TX_SIZE)) return Res::Err("evm tx size too large"); - if (!evm_validate_raw_tx(HexStr(obj.evmTx))) { - return Res::Err("evm tx failed to validate"); + RustRes result; + evm_try_prevalidate_raw_tx(result, HexStr(obj.evmTx)); + + if (!result.ok) { + LogPrintf("[evm_try_prevalidate_raw_tx] failed, reason : %s\n", result.reason); + return Res::Err("evm tx failed to validate %s", result.reason); } - if (!evm_queue_tx(evmContext, HexStr(obj.evmTx))) { - return Res::Err("evm tx failed to queue"); + evm_try_queue_tx(result, evmContext, HexStr(obj.evmTx), tx.GetHash().ToArrayReversed()); + if (!result.ok) { + LogPrintf("[evm_try_queue_tx] failed, reason : %s\n", result.reason); + return Res::Err("evm tx failed to queue %s\n", result.reason); } return Res::Ok(); @@ -3922,9 +3928,9 @@ Res ValidateTransferDomain(const CTransaction &tx, CTxDestination dest; - // Soruce validation + // Source validation // Check domain type - if (src.domain == VMDomain::DVM) { + if (src.domain == static_cast(VMDomain::DVM)) { // Reject if source address is ETH address if (ExtractDestination(src.address, dest)) { if (dest.index() == WitV16KeyEthHashType) { @@ -3935,7 +3941,7 @@ Res ValidateTransferDomain(const CTransaction &tx, res = HasAuth(tx, coins, src.address); if (!res) return res; - } else if (src.domain == VMDomain::EVM) { + } else if (src.domain == static_cast(VMDomain::EVM)) { // Reject if source address is DFI address if (ExtractDestination(src.address, dest)) { if (dest.index() != WitV16KeyEthHashType) { @@ -3951,14 +3957,14 @@ Res ValidateTransferDomain(const CTransaction &tx, // Destination validation // Check domain type - if (dst.domain == VMDomain::DVM) { + if (dst.domain == static_cast(VMDomain::DVM)) { // Reject if source address is ETH address if (ExtractDestination(dst.address, dest)) { if (dest.index() == WitV16KeyEthHashType) { return DeFiErrors::TransferDomainETHDestinationAddress(); } } - } else if (dst.domain == VMDomain::EVM) { + } else if (dst.domain == static_cast(VMDomain::EVM)) { // Reject if source address is DFI address if (ExtractDestination(dst.address, dest)) { if (dest.index() != WitV16KeyEthHashType) { diff --git a/src/masternodes/mn_rpc.h b/src/masternodes/mn_rpc.h index 5f1d3e2062f..9577bff824a 100644 --- a/src/masternodes/mn_rpc.h +++ b/src/masternodes/mn_rpc.h @@ -77,6 +77,7 @@ CMutableTransaction fund(CMutableTransaction &mtx, CTransactionRef send(CTransactionRef tx, CTransactionRef optAuthTx); CTransactionRef sign(CMutableTransaction& mtx, CWallet* const pwallet, CTransactionRef optAuthTx); CTransactionRef signsend(CMutableTransaction &mtx, CWalletCoinsUnlocker &pwallet, CTransactionRef optAuthTx); +CTransactionRef send(CTransactionRef tx, CTransactionRef optAuthTx); CWalletCoinsUnlocker GetWallet(const JSONRPCRequest &request); std::vector GetAuthInputsSmart(CWalletCoinsUnlocker &pwallet, int32_t txVersion, diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index 22ed931a0ba..9d1d0834834 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -472,21 +472,38 @@ UniValue getaccount(const JSONRPCRequest& request) { mnview.CalculateOwnerRewards(reqOwner, targetHeight); + std::map balances{}; + CTxDestination dest; + if (ExtractDestination(reqOwner, dest) && dest.index() == WitV16KeyEthHashType) { + const auto keyID = std::get(dest); + std::array address{}; + std::copy(keyID.begin(), keyID.end(), address.begin()); + if (const auto balance = evm_get_balance(address)) { + balances[DCT_ID{}] = balance; + } + } + mnview.ForEachBalance( [&](const CScript &owner, CTokenAmount balance) { if (owner != reqOwner) { return false; } - if (indexed_amounts) - ret.pushKV(balance.nTokenId.ToString(), ValueFromAmount(balance.nValue)); - else - ret.push_back(tokenAmountString(balance)); + balances[balance.nTokenId] += balance.nValue; limit--; return limit != 0; }, BalanceKey{reqOwner, start}); + + for (const auto& [id, amount] : balances) { + if (indexed_amounts) { + ret.pushKV(id.ToString(), ValueFromAmount(amount)); + } else { + ret.push_back(tokenAmountString({id, amount})); + } + } + return GetRPCResultCache().Set(request, ret); } @@ -586,7 +603,9 @@ UniValue gettokenbalances(const JSONRPCRequest& request) { if (eth_lookup) { for (const auto keyID : pwallet->GetEthKeys()) { - const auto evmAmount = evm_get_balance(HexStr(keyID.begin(), keyID.end())); + std::array address{}; + std::copy(keyID.begin(), keyID.end(), address.begin()); + const auto evmAmount = evm_get_balance(address); totalBalances.Add({{}, static_cast(evmAmount)}); } } @@ -654,6 +673,10 @@ UniValue utxostoaccount(const JSONRPCRequest& request) { CUtxosToAccountMessage msg{}; msg.to = DecodeRecipientsDefaultInternal(pwallet, request.params[0].get_obj()); + for (const auto& [to, amount] : msg.to) { + RejectEthAddress(to); + } + // encode CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::UtxosToAccount) @@ -808,6 +831,11 @@ UniValue accounttoaccount(const JSONRPCRequest& request) { msg.from = DecodeScript(request.params[0].get_str()); + for (const auto& [to, amount] : msg.to) { + RejectEthAddress(to); + } + RejectEthAddress(msg.from); + // encode CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::AccountToAccount) @@ -894,6 +922,7 @@ UniValue accounttoutxos(const JSONRPCRequest& request) { // decode sender and recipients CAccountToUtxosMessage msg{}; msg.from = DecodeScript(request.params[0].get_str()); + RejectEthAddress(msg.from); const auto to = DecodeRecipients(pwallet->chain(), request.params[1]); msg.balances = SumAllTransfers(to); if (msg.balances.balances.empty()) { @@ -1901,6 +1930,13 @@ UniValue sendtokenstoaddress(const JSONRPCRequest& request) { msg.from = DecodeRecipients(pwallet->chain(), request.params[0].get_obj()); } + for (const auto& [to, amount] : msg.to) { + RejectEthAddress(to); + } + for (const auto& [from, amount] : msg.from) { + RejectEthAddress(from); + } + // encode CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::AnyAccountsToAccounts) @@ -1987,8 +2023,8 @@ UniValue transferdomain(const JSONRPCRequest& request) { "\"hash\" (string) The hex-encoded hash of broadcasted transaction\n" }, RPCExamples{ - HelpExampleCli("transferdomain", R"('[{"src":{"address":, "amount\":"1.0@DFI", "domain": 1}, {"dst":{"address":, "amount":"1.0@DFI", "domain": 2}}]')") + - HelpExampleCli("transferdomain", R"('[{"src":{"address":, "amount\":"1.0@DFI", "domain": 2}, {"dst":{"address":, "amount":"1.0@DFI", "domain": 1}}]')") + HelpExampleCli("transferdomain", R"('[{"src":{"address":"", "amount":"1.0@DFI", "domain": 1}, "dst":{"address":"", "amount":"1.0@DFI", "domain": 2}}]')") + + HelpExampleCli("transferdomain", R"('[{"src":{"address":"", "amount":"1.0@DFI", "domain": 2}, "dst":{"address":"", "amount":"1.0@DFI", "domain": 1}}]')") }, }.Check(request); @@ -2027,18 +2063,18 @@ UniValue transferdomain(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, src argument \"amount\" must not be null"); if (!srcObj["domain"].isNull()) - src.domain = static_cast(srcObj["domain"].get_int()); + src.domain = srcObj["domain"].get_int(); else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, src argument \"domain\" must not be null"); - if (src.domain == VMDomain::DVM) { + if (src.domain == static_cast(VMDomain::DVM)) { auths.insert(src.address); - } else if (src.domain == VMDomain::EVM) { + } else if (src.domain == static_cast(VMDomain::EVM)) { const auto key = AddrToPubKey(pwallet, ScriptToString(src.address)); const auto auth = GetScriptForDestination(PKHash(key.GetID())); auths.insert(auth); } else - throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, src argument \"domain\" must be either 1 (DFI token to EVM) or 2 (EVM to DFI token)"); + throw JSONRPCError(RPC_INVALID_PARAMETER,strprintf("Invalid parameters, src argument \"domain\" must be either %d (DFI token to EVM) or %d (EVM to DFI token)", static_cast(VMDomain::DVM), static_cast(VMDomain::EVM))); if (!srcObj["data"].isNull()) src.data.assign(srcObj["data"].getValStr().begin(), srcObj["data"].getValStr().end()); @@ -2054,7 +2090,7 @@ UniValue transferdomain(const JSONRPCRequest& request) { throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, dst argument \"amount\" must not be null"); if (!dstObj["domain"].isNull()) - dst.domain = static_cast(dstObj["domain"].get_int()); + dst.domain = dstObj["domain"].get_int(); else throw JSONRPCError(RPC_INVALID_PARAMETER,"Invalid parameters, dst argument \"domain\" must not be null"); @@ -2387,6 +2423,7 @@ UniValue HandleSendDFIP2201BTCInput(const JSONRPCRequest& request, CWalletCoinsU throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } const auto script = GetScriptForDestination(dest); + RejectEthAddress(script); CSmartContractMessage msg{}; msg.name = contractPair.first; @@ -2528,6 +2565,8 @@ UniValue futureswap(const JSONRPCRequest& request) { msg.owner = GetScriptForDestination(dest); msg.source = DecodeAmount(pwallet->chain(), request.params[1], ""); + RejectEthAddress(msg.owner); + if (!request.params[2].isNull()) { DCT_ID destTokenID{}; @@ -2630,6 +2669,8 @@ UniValue withdrawfutureswap(const JSONRPCRequest& request) { msg.destination = destTokenID.v; } + RejectEthAddress(msg.owner); + // Encode CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); metadata << static_cast(CustomTxType::FutureSwap) diff --git a/src/masternodes/rpc_customtx.cpp b/src/masternodes/rpc_customtx.cpp index 09e7ada520d..8604f5a7136 100644 --- a/src/masternodes/rpc_customtx.cpp +++ b/src/masternodes/rpc_customtx.cpp @@ -561,7 +561,7 @@ class CCustomTxRpcVisitor { for (auto &[j, o]: items) { j.pushKV("address", ScriptToString(o.address)); j.pushKV("amount", o.amount.ToString()); - j.pushKV("domain", CTransferDomainToString(static_cast(o.domain))); + j.pushKV("domain", CTransferDomainToString(VMDomain(o.domain))); if (!o.data.empty()) { j.pushKV("data", std::string(o.data.begin(), o.data.end())); } diff --git a/src/masternodes/rpc_loan.cpp b/src/masternodes/rpc_loan.cpp index 1f03e1e939c..a9f1efbc89d 100644 --- a/src/masternodes/rpc_loan.cpp +++ b/src/masternodes/rpc_loan.cpp @@ -1075,6 +1075,8 @@ UniValue takeloan(const JSONRPCRequest& request) { if (!metaObj["to"].isNull()) takeLoan.to = DecodeScript(metaObj["to"].getValStr()); + RejectEthAddress(takeLoan.to); + if (!metaObj["amounts"].isNull()) takeLoan.amounts = DecodeAmounts(pwallet->chain(), metaObj["amounts"], ""); else @@ -1265,6 +1267,8 @@ UniValue paybackloan(const JSONRPCRequest& request) { } else from = DecodeScript(metaObj["from"].getValStr()); + RejectEthAddress(from); + if (!::IsMine(*pwallet, from)) throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Address (%s) is not owned by the wallet", metaObj["from"].getValStr())); diff --git a/src/masternodes/rpc_poolpair.cpp b/src/masternodes/rpc_poolpair.cpp index 43540c1353b..13557679897 100644 --- a/src/masternodes/rpc_poolpair.cpp +++ b/src/masternodes/rpc_poolpair.cpp @@ -404,6 +404,11 @@ UniValue addpoolliquidity(const JSONRPCRequest &request) { } msg.shareAddress = DecodeScript(request.params[1].get_str()); + for (const auto& [from, balance] : msg.from) { + RejectEthAddress(from); + } + RejectEthAddress(msg.shareAddress); + // encode CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::AddPoolLiquidity) << msg; @@ -497,6 +502,8 @@ UniValue removepoolliquidity(const JSONRPCRequest &request) { msg.from = DecodeScript(from); msg.amount = DecodeAmount(pwallet->chain(), amount, from); + RejectEthAddress(msg.from); + // encode CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::RemovePoolLiquidity) << msg; @@ -636,6 +643,7 @@ UniValue createpoolpair(const JSONRPCRequest &request) { if (!metadataObj["customRewards"].isNull()) { rewards = DecodeAmounts(pwallet->chain(), metadataObj["customRewards"], ""); } + RejectEthAddress(ownerAddress); int targetHeight; DCT_ID idtokenA, idtokenB; @@ -815,6 +823,7 @@ UniValue updatepoolpair(const JSONRPCRequest &request) { std::numeric_limits::max())); } } + RejectEthAddress(ownerAddress); const auto txVersion = GetTransactionVersion(targetHeight); CMutableTransaction rawTx(txVersion); @@ -932,6 +941,9 @@ UniValue poolswap(const JSONRPCRequest &request) { CheckAndFillPoolSwapMessage(request, poolSwapMsg); int targetHeight = chainHeight(*pwallet->chain().lock()) + 1; + RejectEthAddress(poolSwapMsg.from); + RejectEthAddress(poolSwapMsg.to); + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); metadata << static_cast(CustomTxType::PoolSwap); metadata << poolSwapMsg; @@ -1050,6 +1062,9 @@ UniValue compositeswap(const JSONRPCRequest &request) { CPoolSwapMessage &poolSwapMsg = poolSwapMsgV2.swapInfo; CheckAndFillPoolSwapMessage(request, poolSwapMsg); + RejectEthAddress(poolSwapMsg.from); + RejectEthAddress(poolSwapMsg.to); + { LOCK(cs_main); // If no direct swap found search for composite swap diff --git a/src/masternodes/rpc_proposals.cpp b/src/masternodes/rpc_proposals.cpp index 658715494a0..8b3009f516e 100644 --- a/src/masternodes/rpc_proposals.cpp +++ b/src/masternodes/rpc_proposals.cpp @@ -269,6 +269,8 @@ UniValue creategovcfp(const JSONRPCRequest &request) { pm.contextHash = contextHash; pm.options = 0; + RejectEthAddress(pm.address); + // encode CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); metadata << static_cast(CustomTxType::CreateCfp) << pm; diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index 409861b92c9..7627401dd23 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -303,6 +303,8 @@ UniValue createvault(const JSONRPCRequest& request) { CVaultMessage vault; vault.ownerAddress = DecodeScript(request.params[0].getValStr()); + RejectEthAddress(vault.ownerAddress); + if (request.params.size() > 1) { if (!request.params[1].isNull()) { vault.schemeId = request.params[1].get_str(); @@ -407,6 +409,8 @@ UniValue closevault(const JSONRPCRequest& request) { msg.to = DecodeScript(request.params[1].getValStr()); + RejectEthAddress(msg.to); + CDataStream metadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); metadata << static_cast(CustomTxType::CloseVault) << msg; @@ -713,6 +717,9 @@ UniValue updatevault(const JSONRPCRequest& request) { } msg.ownerAddress = DecodeScript(ownerAddress); } + + RejectEthAddress(msg.ownerAddress); + if(!params["loanSchemeId"].isNull()){ auto loanschemeid = params["loanSchemeId"].getValStr(); msg.schemeId = loanschemeid; @@ -801,6 +808,7 @@ UniValue deposittovault(const JSONRPCRequest& request) { // decode vaultId CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); auto from = DecodeScript(request.params[1].get_str()); + RejectEthAddress(from); CTokenAmount amount = DecodeAmount(pwallet->chain(),request.params[2].get_str(), "amount"); CDepositToVaultMessage msg{vaultId, from, amount}; @@ -887,6 +895,7 @@ UniValue withdrawfromvault(const JSONRPCRequest& request) { // decode vaultId CVaultId vaultId = ParseHashV(request.params[0], "vaultId"); auto to = DecodeScript(request.params[1].get_str()); + RejectEthAddress(to); CTokenAmount amount = DecodeAmount(pwallet->chain(),request.params[2].get_str(), "amount"); CWithdrawFromVaultMessage msg{vaultId, to, amount}; @@ -1005,6 +1014,8 @@ UniValue placeauctionbid(const JSONRPCRequest& request) { from = DecodeScript(fromStr); } + RejectEthAddress(from); + CAuctionBidMessage msg{vaultId, index, from, amount}; CDataStream markedMetadata(DfTxMarker, SER_NETWORK, PROTOCOL_VERSION); markedMetadata << static_cast(CustomTxType::AuctionBid) diff --git a/src/masternodes/validation.cpp b/src/masternodes/validation.cpp index 90e110576d4..be3777248b4 100644 --- a/src/masternodes/validation.cpp +++ b/src/masternodes/validation.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -2355,9 +2356,9 @@ static void ProcessGrandCentralEvents(const CBlockIndex* pindex, CCustomCSView& static void RevertTransferDomain(const CTransferDomainMessage &obj, CCustomCSView &mnview) { // NOTE: Each domain's revert is handle by it's own domain module. This function reverts only the DVM aspect. EVM will handle it's own revert. for (const auto &[src, dst] : obj.transfers) { - if (src.domain == VMDomain::DVM) + if (src.domain == static_cast(VMDomain::DVM)) mnview.AddBalance(src.address, src.amount); - if (dst.domain == VMDomain::DVM) + if (dst.domain == static_cast(VMDomain::DVM)) mnview.SubBalance(dst.address, dst.amount); } } @@ -2383,7 +2384,65 @@ static void RevertFailedTransferDomainTxs(const std::vector &failed } } -void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs) { +static void ProcessEVMQueue(const CBlock &block, const CBlockIndex *pindex, CCustomCSView &cache, const CChainParams& chainparams, const uint64_t evmContext, std::array& beneficiary) { + + if (IsEVMEnabled(pindex->nHeight, cache)) { + CKeyID minter; + assert(block.ExtractMinterKey(minter)); + CScript minerAddress; + + if (!fMockNetwork) { + const auto id = cache.GetMasternodeIdByOperator(minter); + assert(id); + const auto node = cache.GetMasternode(*id); + assert(node); + + auto height = node->creationHeight; + auto mnID = *id; + if (!node->collateralTx.IsNull()) { + const auto idHeight = cache.GetNewCollateral(node->collateralTx); + assert(idHeight); + height = idHeight->blockHeight - GetMnResignDelay(std::numeric_limits::max()); + mnID = node->collateralTx; + } + + const auto blockindex = ::ChainActive()[height]; + assert(blockindex); + + CTransactionRef tx; + uint256 hash_block; + assert(GetTransaction(mnID, tx, Params().GetConsensus(), hash_block, blockindex)); + assert(tx->vout.size() >= 2); + + CTxDestination dest; + assert(ExtractDestination(tx->vout[1].scriptPubKey, dest)); + assert(dest.index() == PKHashType || dest.index() == WitV0KeyHashType); + + const auto keyID = dest.index() == PKHashType ? CKeyID(std::get(dest)) : CKeyID(std::get(dest)); + std::copy(keyID.begin(), keyID.end(), beneficiary.begin()); + minerAddress = GetScriptForDestination(dest); + } else { + std::copy(minter.begin(), minter.end(), beneficiary.begin()); + const auto dest = PKHash(minter); + minerAddress = GetScriptForDestination(dest); + } + + const auto blockResult = evm_finalize(evmContext, false, block.nBits, beneficiary, block.GetBlockTime()); + + if (!blockResult.failed_transactions.empty()) { + std::vector failedTransactions; + for (const auto& rust_string : blockResult.failed_transactions) { + failedTransactions.emplace_back(rust_string.data(), rust_string.length()); + } + + RevertFailedTransferDomainTxs(failedTransactions, block, chainparams.GetConsensus(), pindex->nHeight, cache); + } + + cache.AddBalance(minerAddress, {DCT_ID{}, static_cast(blockResult.miner_fee / CAMOUNT_TO_GWEI)}); + } +} + +void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs, const uint64_t evmContext, std::array& beneficiary) { CCustomCSView cache(mnview); // calculate rewards to current block @@ -2437,6 +2496,9 @@ void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSV // Migrate foundation members to attributes ProcessGrandCentralEvents(pindex, cache, chainparams); + // Execute EVM Queue + ProcessEVMQueue(block, pindex, cache, chainparams, evmContext, beneficiary); + // construct undo auto& flushable = cache.GetStorage(); auto undo = CUndo::Construct(mnview.GetStorage(), flushable.GetRaw()); diff --git a/src/masternodes/validation.h b/src/masternodes/validation.h index 73c1db4eb2b..0e690cfda80 100644 --- a/src/masternodes/validation.h +++ b/src/masternodes/validation.h @@ -17,7 +17,7 @@ class CCustomCSView; using CreationTxs = std::map>>>; -void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs); +void ProcessDeFiEvent(const CBlock &block, const CBlockIndex* pindex, CCustomCSView& mnview, const CCoinsViewCache& view, const CChainParams& chainparams, const CreationTxs &creationTxs, const uint64_t evmContext, std::array& beneficiary); std::vector CollectAuctionBatches(const CVaultAssets& vaultAssets, const TAmounts& collBalances, const TAmounts& loanBalances); diff --git a/src/miner.cpp b/src/miner.cpp index 783509749c8..f0365ff1aa0 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -33,10 +34,33 @@ #include #include -#include #include #include +struct EVM { + uint256 blockHash; + uint64_t minerFee; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(blockHash); + READWRITE(minerFee); + } +}; + +struct XVM { + EVM evm; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(evm); + } +}; + int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int64_t nOldTime = pblock->nTime; @@ -101,9 +125,9 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc resetBlock(); - pblocktemplate.reset(new CBlockTemplate()); + pblocktemplate = std::make_unique(); - if(!pblocktemplate.get()) + if(!pblocktemplate) return nullptr; pblock = &pblocktemplate->block; // pointer for convenience @@ -116,16 +140,14 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc CBlockIndex* pindexPrev = ::ChainActive().Tip(); assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; - // in fact, this may be redundant cause it was checked upthere in the miner - std::optional> myIDs; - std::optional nodePtr; - if (!blockTime) { - myIDs = pcustomcsview->AmIOperator(); - if (!myIDs) - return nullptr; - nodePtr = pcustomcsview->GetMasternode(myIDs->second); - if (!nodePtr || !nodePtr->IsActive(nHeight, *pcustomcsview)) - return nullptr; + + const auto myIDs = pcustomcsview->AmIOperator(); + if (!myIDs) { + return nullptr; + } + const auto nodePtr = pcustomcsview->GetMasternode(myIDs->second); + if (!nodePtr || !nodePtr->IsActive(nHeight, *pcustomcsview)) { + return nullptr; } auto consensus = chainparams.GetConsensus(); @@ -236,20 +258,31 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } const auto evmContext = evm_get_context(); + std::map txFees; if (timeOrdering) { - addPackageTxs(nPackagesSelected, nDescendantsUpdated, nHeight, mnview, evmContext); + addPackageTxs(nPackagesSelected, nDescendantsUpdated, nHeight, mnview, evmContext, txFees); } else { - addPackageTxs(nPackagesSelected, nDescendantsUpdated, nHeight, mnview, evmContext); + addPackageTxs(nPackagesSelected, nDescendantsUpdated, nHeight, mnview, evmContext, txFees); } - // TODO Get failed TXs and try to restore to mempool - std::vector evmHeader{}; + XVM xvm{}; if (IsEVMEnabled(nHeight, mnview)) { - std::array dummyAddress{}; - const auto rustHeader = evm_finalize(evmContext, false, pos::GetNextWorkRequired(pindexPrev, pblock->nTime, consensus), dummyAddress); - evmHeader.resize(rustHeader.size()); - std::copy(rustHeader.begin(), rustHeader.end(), evmHeader.begin()); + std::array beneficiary{}; + std::copy(nodePtr->ownerAuthAddress.begin(), nodePtr->ownerAuthAddress.end(), beneficiary.begin()); + auto blockResult = evm_finalize(evmContext, false, pos::GetNextWorkRequired(pindexPrev, pblock->nTime, consensus), beneficiary, blockTime); + evm_discard_context(evmContext); + + const auto blockHash = std::vector(blockResult.block_hash.begin(), blockResult.block_hash.end()); + + xvm = XVM{{uint256(blockHash), blockResult.miner_fee / CAMOUNT_TO_GWEI}}; + + std::vector failedTransactions; + for (const auto& rust_string : blockResult.failed_transactions) { + failedTransactions.emplace_back(rust_string.data(), rust_string.length()); + } + + RemoveFailedTransactions(failedTransactions, txFees); } // TXs for the creationTx field in new tokens created via token split @@ -326,13 +359,17 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc coinbaseTx.vout[0].nValue = CalculateCoinbaseReward(blockReward, consensus.dist.masternode); } - if (IsEVMEnabled(nHeight, mnview) && !evmHeader.empty()) { + if (IsEVMEnabled(nHeight, mnview) && !xvm.evm.blockHash.IsNull()) { const auto headerIndex = coinbaseTx.vout.size(); coinbaseTx.vout.resize(headerIndex + 1); coinbaseTx.vout[headerIndex].nValue = 0; + CDataStream metadata(SER_NETWORK, PROTOCOL_VERSION); + metadata << xvm; + CScript script; - script << OP_RETURN << evmHeader; + script << OP_RETURN << ToByteVector(metadata); + coinbaseTx.vout[headerIndex].scriptPubKey = script; } @@ -382,7 +419,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblock->hashPrevBlock = pindexPrev->GetBlockHash(); pblock->deprecatedHeight = pindexPrev->nHeight + 1; pblock->nBits = pos::GetNextWorkRequired(pindexPrev, pblock->nTime, consensus); - if (myIDs) { + if (!blockTime) { pblock->stakeModifier = pos::ComputeStakeModifier(pindexPrev->stakeModifier, myIDs->first); } @@ -491,6 +528,70 @@ int BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& already return nDescendantsUpdated; } +void BlockAssembler::RemoveFailedTransactions(const std::vector &failedTransactions, const std::map &txFees) { + if (failedTransactions.empty()) { + return; + } + + std::vector txsToErase; + for (const auto &txStr : failedTransactions) { + const auto failedHash = uint256S(txStr); + for (const auto &tx : pblock->vtx) { + if (tx && tx->GetHash() == failedHash) { + std::vector metadata; + const auto txType = GuessCustomTxType(*tx, metadata, false); + if (txType == CustomTxType::TransferDomain) { + txsToErase.push_back(failedHash); + } + } + } + } + + // Get a copy of the TXs to be erased for restoring to the mempool later + std::vector txsToRemove; + std::set txsToEraseSet(txsToErase.begin(), txsToErase.end()); + + for (const auto &tx : pblock->vtx) { + if (tx && txsToEraseSet.count(tx->GetHash())) { + txsToRemove.push_back(tx); + } + } + + // Add descendants and in turn add their descendants. This needs to + // be done in the order that the TXs are in the block for txsToRemove. + auto size = txsToErase.size(); + for (std::vector::size_type i{}; i < size; ++i) { + for (const auto &tx : pblock->vtx) { + if (tx) { + for (const auto &vin : tx->vin) { + if (vin.prevout.hash == txsToErase[i] && + std::find(txsToErase.begin(), txsToErase.end(), tx->GetHash()) == txsToErase.end()) + { + txsToRemove.push_back(tx); + txsToErase.push_back(tx->GetHash()); + ++size; + } + } + } + } + } + + // Erase TXs from block + txsToEraseSet = std::set(txsToErase.begin(), txsToErase.end()); + pblock->vtx.erase( + std::remove_if(pblock->vtx.begin(), pblock->vtx.end(), + [&txsToEraseSet](const auto &tx) { + return tx && txsToEraseSet.count(tx.get()->GetHash()); + }),pblock->vtx.end()); + + for (const auto &tx : txsToRemove) { + // Remove fees. + if (txFees.count(tx->GetHash())) { + nFees -= txFees.at(tx->GetHash()); + } + } +} + // Skip entries in mapTx that are already in a block or are present // in mapModifiedTx (which implies that the mapTx ancestor state is // stale due to ancestor inclusion in the block) @@ -528,13 +629,15 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve // mapModifiedTxs with the next transaction in the mempool to decide what // transaction package to work on next. template -void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, int nHeight, CCustomCSView &view, const uint64_t evmContext) +void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, int nHeight, CCustomCSView &view, const uint64_t evmContext, std::map &txFees) { // mapModifiedTx will store sorted packages after they are modified // because some of their txs are already in the block indexed_modified_transaction_set mapModifiedTx; // Keep track of entries that failed inclusion, to avoid duplicate work CTxMemPool::setEntries failedTx; + // Keep track of EVM entries that failed nonce check + std::multimap failedNonces; // Start by adding all descendants of previously added txs to mapModifiedTx // and modifying them for their already included ancestors @@ -555,7 +658,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda // Copy of the view CCoinsViewCache coinsView(&::ChainstateActive().CoinsTip()); - while (mi != mempool.mapTx.get().end() || !mapModifiedTx.empty()) + while (mi != mempool.mapTx.get().end() || !mapModifiedTx.empty() || !failedNonces.empty()) { // First try to find a new transaction in mapTx to evaluate. if (mi != mempool.mapTx.get().end() && @@ -569,7 +672,11 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda bool fUsingModified = false; modtxscoreiter modit = mapModifiedTx.get().begin(); - if (mi == mempool.mapTx.get().end()) { + if (mi == mempool.mapTx.get().end() && mapModifiedTx.empty()) { + const auto it = failedNonces.begin(); + iter = it->second; + failedNonces.erase(it); + } else if (mi == mempool.mapTx.get().end()) { // We're out of entries in mapTx; use the entry from mapModifiedTx iter = modit->iter; fUsingModified = true; @@ -603,7 +710,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda packageSigOpsCost = modit->nSigOpCostWithAncestors; } - if (packageFees < blockMinFeeRate.GetFee(packageSize)) { + if (!IsEVMTx(iter->GetTx()) && packageFees < blockMinFeeRate.GetFee(packageSize)) { // Everything else we might consider has a lower fee rate break; } @@ -672,11 +779,38 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda AddCoins(coins, tx, nHeight, false); // do not check std::vector metadata; - CustomTxType txType = GuessCustomTxType(tx, metadata); + CustomTxType txType = GuessCustomTxType(tx, metadata, true); // Only check custom TXs if (txType != CustomTxType::None) { - auto res = ApplyCustomTx(view, coins, tx, chainparams.GetConsensus(), nHeight, pblock->nTime, nullptr, 0, evmContext); + if (txType == CustomTxType::EvmTx) { + auto txMessage = customTypeToMessage(txType); + if (!CustomMetadataParse(nHeight, Params().GetConsensus(), metadata, txMessage)) { + customTxPassed = false; + break; + } + + const auto obj = std::get(txMessage); + + RustRes result; + const auto txResult = evm_try_prevalidate_raw_tx(result, HexStr(obj.evmTx)); + if (!result.ok) { + customTxPassed = false; + break; + } + + const auto nonce = evm_get_next_valid_nonce_in_context(evmContext, txResult.sender); + if (nonce != txResult.nonce) { + // Only add if not already in failed TXs to prevent adding on second attempt. + if (!failedTx.count(iter)) { + failedNonces.emplace(nonce, iter); + } + customTxPassed = false; + break; + } + } + + const auto res = ApplyCustomTx(view, coins, tx, chainparams.GetConsensus(), nHeight, pblock->nTime, nullptr, 0, evmContext); // Not okay invalidate, undo and skip if (!res.ok) { @@ -703,6 +837,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda } for (size_t i=0; iGetTx().GetHash(), sortedEntries[i]->GetFee()); AddToBlock(sortedEntries[i]); // Erase from the modified set, if present mapModifiedTx.erase(sortedEntries[i]); diff --git a/src/miner.h b/src/miner.h index a00b2784c1f..78055a0ddfe 100644 --- a/src/miner.h +++ b/src/miner.h @@ -193,7 +193,7 @@ class BlockAssembler * Increments nPackagesSelected / nDescendantsUpdated with corresponding * statistics from the package selection (for logging statistics). */ template - void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, int nHeight, CCustomCSView &view, const uint64_t evmContext) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); + void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated, int nHeight, CCustomCSView &view, const uint64_t evmContext, std::map &txFees) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); // helper functions for addPackageTxs() /** Remove confirmed (inBlock) entries from given set */ @@ -214,6 +214,8 @@ class BlockAssembler * state updated assuming given transactions are inBlock. Returns number * of updated descendants. */ int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); + /** Remove failed EVM transactions from the block */ + void RemoveFailedTransactions(const std::vector &failedTransactions, const std::map &txFees); }; /** Modify the extranonce in a block */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c839f26ed27..6b616465189 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -4156,7 +4157,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) if (!txinfo.tx) { continue; } - if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { + if (!IsEVMTx(*txinfo.tx) && filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { continue; } if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 64854c5faed..14cd51481c3 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -55,7 +55,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) std::vector > vSolutions; whichType = Solver(scriptPubKey, vSolutions); - if (whichType == TX_NONSTANDARD) { + if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_V16_ETHHASH) { return false; } else if (whichType == TX_MULTISIG) { unsigned char m = vSolutions.front()[0]; @@ -113,7 +113,11 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR txnouttype whichType; for (const CTxOut& txout : tx.vout) { if (!::IsStandard(txout.scriptPubKey, whichType)) { - reason = "scriptpubkey"; + if (whichType == txnouttype::TX_WITNESS_V16_ETHHASH) { + reason = "eth-scriptpubkey"; + } else { + reason = "scriptpubkey"; + } return false; } diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 37b4264db96..60930cb5477 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -361,6 +361,10 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listgovproposalvotes", 3, "pagination" }, { "listgovproposals", 2, "cycle" }, { "listgovproposals", 3, "pagination" }, + { "evmtx", 1, "nonce" }, + { "evmtx", 2, "gasPrice" }, + { "evmtx", 3, "gasLimit" }, + { "evmtx", 5, "value" }, }; // clang-format on diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 427da3ccc47..33bc2195608 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -86,6 +86,13 @@ CScript DecodeScript(std::string const& str) return GetScriptForDestination(dest); } +void RejectEthAddress(const CScript &address) { + CTxDestination dest; + if (ExtractDestination(address, dest) && dest.index() == WitV16KeyEthHashType) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Eth type addresses are not valid"); + } +} + int DecodeScriptTxId(const std::string& str, CParserResults result) { if (IsHex(str)) { diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index a5b26107796..b10afad3ad8 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -28,6 +28,7 @@ ResVal> ParseTokenAmount(std::string const & tok ResVal GuessTokenAmount(interfaces::Chain const & chain,std::string const & tokenAmount); CScript DecodeScript(std::string const& str); +void RejectEthAddress(const CScript &address); CTokenAmount DecodeAmount(interfaces::Chain const & chain, UniValue const& amountUni, std::string const& name); CBalances DecodeAmounts(interfaces::Chain const & chain, UniValue const& amountsUni, std::string const& name); CAccounts DecodeRecipients(interfaces::Chain const & chain, UniValue const& sendTo); diff --git a/src/rpc/register.h b/src/rpc/register.h index f93b24fc380..570cbc7a75b 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -43,7 +43,8 @@ void RegisterICXOrderbookRPCCommands(CRPCTable &tableRPC); void RegisterLoanRPCCommands(CRPCTable &tableRPC); /** Register Vault RPC commands */ void RegisterVaultRPCCommands(CRPCTable &tableRPC); - +/** Register EVM RPC commands */ +void RegisterEVMRPCCommands(CRPCTable &tableRPC); static inline void RegisterAllCoreRPCCommands(CRPCTable &t) { @@ -64,6 +65,7 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t) RegisterICXOrderbookRPCCommands(t); RegisterLoanRPCCommands(t); RegisterVaultRPCCommands(t); + RegisterEVMRPCCommands(t); } #endif // DEFI_RPC_REGISTER_H diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index cab38163e35..fac22f08159 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -84,13 +84,13 @@ void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pu // "Implicitly" refers to fact that scripts are derived automatically from // existing keys, and are present in memory, even without being explicitly // loaded (e.g. from a file). - if (pubkey.IsCompressed()) { - CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id)); - // This does not use AddCScript, as it may be overridden. + if (ethAddress) { + auto script = GetScriptForDestination(WitnessV16EthHash(pubkey)); CScriptID id(script); mapScripts[id] = std::move(script); - } else if (ethAddress) { - auto script = GetScriptForDestination(WitnessV16EthHash(pubkey)); + } else if (pubkey.IsCompressed()) { + CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id)); + // This does not use AddCScript, as it may be overridden. CScriptID id(script); mapScripts[id] = std::move(script); } @@ -112,6 +112,7 @@ bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubke mapKeys[pubkey.GetID()] = key; if (ethAddress) { mapKeys[pubkey.GetEthID()] = key; + mapEthKeys[pubkey.GetEthID()] = key; } ImplicitlyLearnRelatedKeyScripts(pubkey, ethAddress); return true; @@ -154,6 +155,17 @@ bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const return false; } +bool FillableSigningProvider::GetEthKey(const CKeyID &address, CKey &keyOut) const +{ + LOCK(cs_KeyStore); + auto mi = mapEthKeys.find(address); + if (mi != mapEthKeys.end()) { + keyOut = mi->second; + return true; + } + return false; +} + bool FillableSigningProvider::AddCScript(const CScript& redeemScript) { if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) diff --git a/src/script/signingprovider.h b/src/script/signingprovider.h index 4a1c78e2c95..c3e869168f7 100644 --- a/src/script/signingprovider.h +++ b/src/script/signingprovider.h @@ -82,6 +82,7 @@ class FillableSigningProvider : public SigningProvider virtual std::set GetKeys() const; virtual std::set GetEthKeys() const; virtual bool GetKey(const CKeyID &address, CKey &keyOut) const override; + bool GetEthKey(const CKeyID &address, CKey &keyOut) const; virtual bool AddCScript(const CScript& redeemScript); virtual bool HaveCScript(const CScriptID &hash) const override; virtual std::set GetCScripts() const; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index d18cace75cc..6c0eb610c7e 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -351,5 +351,5 @@ CScript GetScriptForHTLC(const CPubKey& seller, const CPubKey& refund, const std } bool IsValidDestination(const CTxDestination& dest) { - return dest.index() != 0; + return dest.index() != NoDestType; } diff --git a/src/spv/bitcoin/BRMerkleBlock.cpp b/src/spv/bitcoin/BRMerkleBlock.cpp index 82b4658551b..da2263e7000 100644 --- a/src/spv/bitcoin/BRMerkleBlock.cpp +++ b/src/spv/bitcoin/BRMerkleBlock.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include diff --git a/src/spv/bitcoin/BRPeerManager.cpp b/src/spv/bitcoin/BRPeerManager.cpp index c5d10304346..315067a4b0a 100644 --- a/src/spv/bitcoin/BRPeerManager.cpp +++ b/src/spv/bitcoin/BRPeerManager.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/spv/bitcoin/BRTransaction.cpp b/src/spv/bitcoin/BRTransaction.cpp index c86c32bdc48..6667a6255b8 100644 --- a/src/spv/bitcoin/BRTransaction.cpp +++ b/src/spv/bitcoin/BRTransaction.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index c95b2b9f3e3..0e7222f9b0d 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -18,7 +18,7 @@ #else #include // for mmap #include // for getrlimit -#include // for PAGESIZE +#include // for PAGESIZE #include // for sysconf #endif diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index f78b357baf9..e9539bd27e5 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include BOOST_FIXTURE_TEST_SUITE(scriptnum_tests, BasicTestingSetup) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4363a47e3fc..0afb15d67be 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -662,22 +662,24 @@ void CTxMemPool::xcheck(const CCoinsViewCache *pcoins, CCustomCSView *mnview, co innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children); bool fDependsWait = false; setEntries setParentCheck; - for (const CTxIn &txin : tx.vin) { - // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. - indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); - if (it2 != mapTx.end()) { - const CTransaction& tx2 = it2->GetTx(); - assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); - fDependsWait = true; - setParentCheck.insert(it2); - } else { - assert(pcoins->HaveCoin(txin.prevout)); + if (!IsEVMTx(tx)) { + for (const CTxIn &txin : tx.vin) { + // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. + indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) { + const CTransaction& tx2 = it2->GetTx(); + assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); + fDependsWait = true; + setParentCheck.insert(it2); + } else { + assert(pcoins->HaveCoin(txin.prevout)); + } + // Check whether its inputs are marked in mapNextTx. + auto it3 = mapNextTx.find(txin.prevout); + assert(it3 != mapNextTx.end()); + assert(it3->first == &txin.prevout); + assert(it3->second == &tx); } - // Check whether its inputs are marked in mapNextTx. - auto it3 = mapNextTx.find(txin.prevout); - assert(it3 != mapNextTx.end()); - assert(it3->first == &txin.prevout); - assert(it3->second == &tx); } assert(setParentCheck == GetMemPoolParents(it)); // Verify ancestor state is correct. diff --git a/src/validation.cpp b/src/validation.cpp index da372f15e4b..91164b10972 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -517,7 +517,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Rather not work on nonstandard transactions (unless -testnet/-regtest) std::string reason; - if (fRequireStandard && !IsStandardTx(tx, reason)) + const auto isStandard{IsStandardTx(tx, reason)}; + if (reason == "eth-scriptpubkey" || (fRequireStandard && !isStandard)) return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, reason); // Do not work on transactions that are too small. @@ -539,38 +540,40 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Check for conflicts with in-memory transactions std::set setConflicts; - for (const CTxIn &txin : tx.vin) - { - const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout); - if (ptxConflicting) { - if (!setConflicts.count(ptxConflicting->GetHash())) - { - // Allow opt-out of transaction replacement by setting - // nSequence > MAX_BIP125_RBF_SEQUENCE (SEQUENCE_FINAL-2) on all inputs. - // - // SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by - // non-replaceable transactions. All inputs rather than just one - // is for the sake of multi-party protocols, where we don't - // want a single party to be able to disable replacement. - // - // The opt-out ignores descendants as anyone relying on - // first-seen mempool behavior should be checking all - // unconfirmed ancestors anyway; doing otherwise is hopelessly - // insecure. - bool fReplacementOptOut = true; - for (const CTxIn &_txin : ptxConflicting->vin) + if (!IsEVMTx(tx)) { + for (const CTxIn &txin : tx.vin) + { + const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout); + if (ptxConflicting) { + if (!setConflicts.count(ptxConflicting->GetHash())) { - if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) + // Allow opt-out of transaction replacement by setting + // nSequence > MAX_BIP125_RBF_SEQUENCE (SEQUENCE_FINAL-2) on all inputs. + // + // SEQUENCE_FINAL-1 is picked to still allow use of nLockTime by + // non-replaceable transactions. All inputs rather than just one + // is for the sake of multi-party protocols, where we don't + // want a single party to be able to disable replacement. + // + // The opt-out ignores descendants as anyone relying on + // first-seen mempool behavior should be checking all + // unconfirmed ancestors anyway; doing otherwise is hopelessly + // insecure. + bool fReplacementOptOut = true; + for (const CTxIn &_txin : ptxConflicting->vin) { - fReplacementOptOut = false; - break; + if (_txin.nSequence <= MAX_BIP125_RBF_SEQUENCE) + { + fReplacementOptOut = false; + break; + } + } + if (fReplacementOptOut) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_DUPLICATE, "txn-mempool-conflict"); } - } - if (fReplacementOptOut) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_DUPLICATE, "txn-mempool-conflict"); - } - setConflicts.insert(ptxConflicting->GetHash()); + setConflicts.insert(ptxConflicting->GetHash()); + } } } } @@ -586,33 +589,35 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool view.SetBackend(viewMemPool); // do all inputs exist? - for (const CTxIn& txin : tx.vin) { - if (!coins_cache.HaveCoinInCache(txin.prevout)) { - coins_to_uncache.push_back(txin.prevout); - } + if (!IsEVMTx(tx)) { + for (const CTxIn& txin : tx.vin) { + if (!coins_cache.HaveCoinInCache(txin.prevout)) { + coins_to_uncache.push_back(txin.prevout); + } - // Note: this call may add txin.prevout to the coins cache - // (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed - // later (via coins_to_uncache) if this tx turns out to be invalid. - if (!view.HaveCoin(txin.prevout)) { - // Are inputs missing because we already have the tx? - for (size_t out = 0; out < tx.vout.size(); out++) { - // Optimistically just do efficient check of cache for outputs - if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); + // Note: this call may add txin.prevout to the coins cache + // (CoinsTip().cacheCoins) by way of FetchCoin(). It should be removed + // later (via coins_to_uncache) if this tx turns out to be invalid. + if (!view.HaveCoin(txin.prevout)) { + // Are inputs missing because we already have the tx? + for (size_t out = 0; out < tx.vout.size(); out++) { + // Optimistically just do efficient check of cache for outputs + if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { + return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); + } } + // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet + if (pfMissingInputs) { + *pfMissingInputs = true; + } + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } - // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet - if (pfMissingInputs) { - *pfMissingInputs = true; - } - return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() - } - // Special check of collateral spending for _not_created_ mn or token (cheating?), those creation tx yet in mempool. CMasternode::CanSpend() (and CheckTxInputs()) will skip this situation - if (txin.prevout.n == 1 && IsMempooledCustomTxCreate(pool, txin.prevout.hash)) { + // Special check of collateral spending for _not_created_ mn or token (cheating?), those creation tx yet in mempool. CMasternode::CanSpend() (and CheckTxInputs()) will skip this situation + if (txin.prevout.n == 1 && IsMempooledCustomTxCreate(pool, txin.prevout.hash)) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "collateral-locked-in-mempool", strprintf("tried to spend collateral of non-created mn or token %s, cheater?", txin.prevout.hash.ToString())); + } } } @@ -634,7 +639,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } // let make sure we have needed coins - if (view.GetValueIn(tx) < nFees) { + if (!IsEVMTx(tx) && view.GetValueIn(tx) < nFees) { return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INVALID, "bad-txns-inputs-below-tx-fee"); } @@ -651,11 +656,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // be mined yet. // Must keep pool.cs for this unless we change CheckSequenceLocks to take a // CoinsViewCache instead of create its own - if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) + if (!IsEVMTx(tx) && !CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final"); // Check for non-standard pay-to-script-hash in inputs - if (fRequireStandard && !AreInputsStandard(tx, view)) + if (fRequireStandard && !IsEVMTx(tx) && !AreInputsStandard(tx, view)) return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); // Check for non-standard witness in P2WSH @@ -693,7 +698,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { + if (!IsEVMTx(tx) && !bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); } @@ -888,7 +893,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // invalid blocks (using TestBlockValidity), however allowing such // transactions into the mempool can be exploited as a DoS attack. unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus()); - if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) { + if (!IsEVMTx(tx) && !CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, true, txdata)) { return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } @@ -1372,6 +1377,9 @@ void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { + if (IsEVMTx(tx)) { + return; + } // mark inputs spent if (!tx.IsCoinBase()) { txundo.vprevout.reserve(tx.vin.size()); @@ -1456,7 +1464,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi return(state.Invalid(ValidationInvalidReason::CONSENSUS, error("CheckBurnSpend: Trying to spend burnt outputs"), REJECT_INVALID, "burnt-output")); } - if (!tx.IsCoinBase()) + if (!tx.IsCoinBase() && !IsEVMTx(tx)) { if (pvChecks) pvChecks->reserve(tx.vin.size()); @@ -1803,7 +1811,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI // restore inputs TBytes dummy; - if (i > 0 && !IsAnchorRewardTx(tx, dummy) && !IsAnchorRewardTxPlus(tx, dummy) && !IsTokenSplitTx(tx, dummy)) { // not coinbases + if (i > 0 && !IsAnchorRewardTx(tx, dummy) && !IsAnchorRewardTxPlus(tx, dummy) && !IsTokenSplitTx(tx, dummy) && !IsEVMTx(tx)) { // not coinbases or EVM TX CTxUndo &txundo = blockUndo.vtxundo[i-1]; if (txundo.vprevout.size() != tx.vin.size()) { error("%s: transaction and undo data inconsistent", __func__); @@ -1851,6 +1859,7 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI mnview.EraseMasternodeLastBlockTime(*nodeId, static_cast(pindex->nHeight)); } } + mnview.SetLastHeight(pindex->pprev->nHeight); return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; @@ -2284,7 +2293,7 @@ static void LogApplyCustomTx(const CTransaction &tx, const int64_t start) { * Validity checks that depend on the UTXO set are also done; ConnectBlock () * can fail if those validity checks fail (among other reasons). */ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, CCustomCSView& mnview, const CChainParams& chainparams, bool & rewardedAnchors, bool fJustCheck, const int64_t evmContext) + CCoinsViewCache& view, CCustomCSView& mnview, const CChainParams& chainparams, bool & rewardedAnchors, std::array& beneficiary, bool fJustCheck, const int64_t evmContext) { AssertLockHeld(cs_main); assert(pindex); @@ -2808,7 +2817,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); - ProcessDeFiEvent(block, pindex, mnview, view, chainparams, creationTxs); + ProcessDeFiEvent(block, pindex, mnview, view, chainparams, creationTxs, evmContext, beneficiary); // Write any UTXO burns for (const auto& [key, value] : writeBurnEntries) @@ -2828,48 +2837,6 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } mnview.SetLastHeight(pindex->nHeight); - if (IsEVMEnabled(pindex->nHeight, mnview)) { - CKeyID minter; - assert(block.ExtractMinterKey(minter)); - std::array minerAddress{}; - - if (!fMockNetwork) { - const auto id = mnview.GetMasternodeIdByOperator(minter); - assert(id); - const auto node = mnview.GetMasternode(*id); - assert(node); - - auto height = node->creationHeight; - auto mnID = *id; - if (!node->collateralTx.IsNull()) { - const auto idHeight = mnview.GetNewCollateral(node->collateralTx); - assert(idHeight); - height = idHeight->blockHeight - GetMnResignDelay(std::numeric_limits::max()); - mnID = node->collateralTx; - } - - const auto blockindex = ::ChainActive()[height]; - assert(blockindex); - - - CTransactionRef tx; - uint256 hash_block; - assert(GetTransaction(mnID, tx, Params().GetConsensus(), hash_block, blockindex)); - assert(tx->vout.size() >= 2); - - CTxDestination dest; - assert(ExtractDestination(tx->vout[1].scriptPubKey, dest)); - assert(dest.index() == PKHashType || dest.index() == WitV0KeyHashType); - - const auto keyID = dest.index() == PKHashType ? CKeyID(std::get(dest)) : CKeyID(std::get(dest)); - std::copy(keyID.begin(), keyID.end(), minerAddress.begin()); - } else { - std::copy(minter.begin(), minter.end(), minerAddress.begin()); - } - - evm_finalize(evmContext, true, block.nBits, minerAddress); - } - auto &checkpoints = chainparams.Checkpoints().mapCheckpoints; auto it = checkpoints.lower_bound(pindex->nHeight); if (it != checkpoints.begin()) { @@ -3171,6 +3138,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha mnview.GetHistoryWriters().DiscardDB(); return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); } + evm_disconnect_latest_block(); bool flushed = view.Flush() && mnview.Flush(); assert(flushed); mnview.GetHistoryWriters().FlushDB(); @@ -3312,8 +3280,9 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp CCoinsViewCache view(&CoinsTip()); CCustomCSView mnview(*pcustomcsview, paccountHistoryDB.get(), pburnHistoryDB.get(), pvaultHistoryDB.get()); bool rewardedAnchors{}; + std::array beneficiary{}; const auto evmContext = evm_get_context(); - bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, mnview, chainparams, rewardedAnchors, evmContext); + bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, mnview, chainparams, rewardedAnchors, beneficiary, false, evmContext); GetMainSignals().BlockChecked(blockConnecting, state); if (!rv) { evm_discard_context(evmContext); @@ -3325,6 +3294,9 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); + if (IsEVMEnabled(pindexNew->nHeight, mnview)) { + evm_finalize(evmContext, true, blockConnecting.nBits, beneficiary, blockConnecting.GetBlockTime()); + } bool flushed = view.Flush() && mnview.Flush(); assert(flushed); mnview.GetHistoryWriters().FlushDB(); @@ -4841,6 +4813,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip()); bool dummyRewardedAnchors{}; + std::array dummyBeneficiary{}; CCustomCSView mnview(*pcustomcsview); uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); @@ -4856,7 +4829,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); - if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, mnview, chainparams, dummyRewardedAnchors, true)) + if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, mnview, chainparams, dummyRewardedAnchors, dummyBeneficiary, true)) return false; assert(state.IsValid()); @@ -5319,7 +5292,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); bool dummyRewardedAnchors{}; - if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, mnview, chainparams, dummyRewardedAnchors)) + std::array dummyBeneficiary{}; + if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, mnview, chainparams, dummyRewardedAnchors, dummyBeneficiary)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); if (ShutdownRequested()) return true; } diff --git a/src/validation.h b/src/validation.h index 2cefd2bd2d1..c997e1e5d6d 100644 --- a/src/validation.h +++ b/src/validation.h @@ -734,7 +734,7 @@ class CChainState { // Block (dis)connection on a given view: DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view, CCustomCSView& cache, std::vector & disconnectedAnchorConfirms); bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, CCustomCSView& cache, const CChainParams& chainparams, bool & rewardedAnchors, bool fJustCheck = false, const int64_t evmContext = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CCoinsViewCache& view, CCustomCSView& cache, const CChainParams& chainparams, bool & rewardedAnchors, std::array& beneficiary, bool fJustCheck = false, const int64_t evmContext = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Apply the effects of a block disconnection on the UTXO set. bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 8037cab77f8..4d9750e8213 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -156,6 +156,23 @@ std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocati return wallet; } +std::array GetKeyFromWallets(std::array input) { + CKey key; + CKeyID keyID; + std::copy(input.begin(), input.end(), keyID.begin()); + + for (const auto &wallet : GetWallets()) { + if (wallet->GetKey(keyID, key)) { + break; + } + } + + std::array result{}; + std::copy(key.begin(), key.end(), result.begin()); + + return result; +} + std::shared_ptr LoadWallet(interfaces::Chain& chain, const std::string& name, std::string& error, std::string& warning) { return LoadWallet(chain, WalletLocation(name), error, warning); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e34c3f3bbd5..ecc7ab39428 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -58,6 +58,7 @@ bool HasWallets(); std::vector> GetWallets(); std::shared_ptr GetWallet(const std::string& name); std::shared_ptr LoadWallet(interfaces::Chain& chain, const WalletLocation& location, std::string& error, std::string& warning); +std::array GetKeyFromWallets(std::array input); CKey GetWalletsKey(CKeyID const & keyid); @@ -1004,7 +1005,7 @@ class CWallet final : public FillableSigningProvider, private interfaces::Chain: //! Adds a key to the store, and saves it to disk. bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey, const bool ethAddress) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, without saving it to disk (used by LoadWallet) - bool LoadKey(const CKey& key, const CPubKey &pubkey, const bool ethAddress = false) { return AddKeyPubKeyInner(key, pubkey, ethAddress); } + bool LoadKey(const CKey& key, const CPubKey &pubkey, const bool ethAddress) { return AddKeyPubKeyInner(key, pubkey, ethAddress); } //! Load metadata (used by LoadWallet) void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index 98dc0fd4be9..86e789ee7e2 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -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'] diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py index ef0bef9e298..0b19bd052ad 100755 --- a/test/lint/check-rpc-mappings.py +++ b/test/lint/check-rpc-mappings.py @@ -21,6 +21,7 @@ "src/wallet/rpcwallet.cpp", "src/masternodes/mn_rpc.cpp", "src/masternodes/rpc_accounts.cpp", + "src/masternodes/rpc_evm.cpp", "src/masternodes/rpc_proposals.cpp", "src/masternodes/rpc_icxorderbook.cpp", "src/masternodes/rpc_loan.cpp", diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 95ae6ae44aa..b1b70b3a5cc 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -10,6 +10,8 @@ KNOWN_VIOLATIONS=( "src/dbwrapper.cpp:.*vsnprintf" "src/httprpc.cpp.*trim" "src/init.cpp:.*atoi" + "src/key_io.cpp:.*toupper*" + "src/key_io.cpp:.*isdigit*" "src/qt/rpcconsole.cpp:.*atoi" "src/rest.cpp:.*strtol" "src/rpc/rawtransaction_util.cpp:.*stoul"