diff --git a/antelope_contracts/contracts/eosio.boot/eosio.boot.abi b/antelope_contracts/contracts/eosio.boot/eosio.boot.abi new file mode 100644 index 0000000..1971165 --- /dev/null +++ b/antelope_contracts/contracts/eosio.boot/eosio.boot.abi @@ -0,0 +1,328 @@ +{ + "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", + "version": "eosio::abi/1.2", + "types": [], + "structs": [ + { + "name": "activate", + "base": "", + "fields": [ + { + "name": "feature_digest", + "type": "checksum256" + } + ] + }, + { + "name": "authority", + "base": "", + "fields": [ + { + "name": "threshold", + "type": "uint32" + }, + { + "name": "keys", + "type": "key_weight[]" + }, + { + "name": "accounts", + "type": "permission_level_weight[]" + }, + { + "name": "waits", + "type": "wait_weight[]" + } + ] + }, + { + "name": "canceldelay", + "base": "", + "fields": [ + { + "name": "canceling_auth", + "type": "permission_level" + }, + { + "name": "trx_id", + "type": "checksum256" + } + ] + }, + { + "name": "deleteauth", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "permission", + "type": "name" + } + ] + }, + { + "name": "key_weight", + "base": "", + "fields": [ + { + "name": "key", + "type": "public_key" + }, + { + "name": "weight", + "type": "uint16" + } + ] + }, + { + "name": "linkauth", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "code", + "type": "name" + }, + { + "name": "type", + "type": "name" + }, + { + "name": "requirement", + "type": "name" + } + ] + }, + { + "name": "newaccount", + "base": "", + "fields": [ + { + "name": "creator", + "type": "name" + }, + { + "name": "name", + "type": "name" + }, + { + "name": "owner", + "type": "authority" + }, + { + "name": "active", + "type": "authority" + } + ] + }, + { + "name": "onerror", + "base": "", + "fields": [ + { + "name": "sender_id", + "type": "uint128" + }, + { + "name": "sent_trx", + "type": "bytes" + } + ] + }, + { + "name": "permission_level", + "base": "", + "fields": [ + { + "name": "actor", + "type": "name" + }, + { + "name": "permission", + "type": "name" + } + ] + }, + { + "name": "permission_level_weight", + "base": "", + "fields": [ + { + "name": "permission", + "type": "permission_level" + }, + { + "name": "weight", + "type": "uint16" + } + ] + }, + { + "name": "reqactivated", + "base": "", + "fields": [ + { + "name": "feature_digest", + "type": "checksum256" + } + ] + }, + { + "name": "setabi", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "abi", + "type": "bytes" + } + ] + }, + { + "name": "setcode", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "vmtype", + "type": "uint8" + }, + { + "name": "vmversion", + "type": "uint8" + }, + { + "name": "code", + "type": "bytes" + } + ] + }, + { + "name": "unlinkauth", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "code", + "type": "name" + }, + { + "name": "type", + "type": "name" + } + ] + }, + { + "name": "updateauth", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "permission", + "type": "name" + }, + { + "name": "parent", + "type": "name" + }, + { + "name": "auth", + "type": "authority" + } + ] + }, + { + "name": "wait_weight", + "base": "", + "fields": [ + { + "name": "wait_sec", + "type": "uint32" + }, + { + "name": "weight", + "type": "uint16" + } + ] + } + ], + "actions": [ + { + "name": "activate", + "type": "activate", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Activate Protocol Feature\nsummary: 'Activate protocol feature {{nowrap feature_digest}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/admin.png#9bf1cec664863bd6aaac0f814b235f8799fb02c850e9aa5da34e8a004bd6518e\n---\n\n{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}." + }, + { + "name": "canceldelay", + "type": "canceldelay", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Cancel Delayed Transaction\nsummary: '{{nowrap canceling_auth.actor}} cancels a delayed transaction'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\n{{canceling_auth.actor}} cancels the delayed transaction with id {{trx_id}}." + }, + { + "name": "deleteauth", + "type": "deleteauth", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Delete Account Permission\nsummary: 'Delete the {{nowrap permission}} permission of {{nowrap account}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\nDelete the {{permission}} permission of {{account}}." + }, + { + "name": "linkauth", + "type": "linkauth", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Link Action to Permission\nsummary: '{{nowrap account}} sets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract to {{nowrap requirement}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\n{{account}} sets the minimum required permission for the {{#if type}}{{type}} action of the{{/if}} {{code}} contract to {{requirement}}.\n\n{{#if type}}{{else}}Any links explicitly associated to specific actions of {{code}} will take precedence.{{/if}}" + }, + { + "name": "newaccount", + "type": "newaccount", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Create New Account\nsummary: '{{nowrap creator}} creates a new account with the name {{nowrap name}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\n{{creator}} creates a new account with the name {{name}} and the following permissions:\n\nowner permission with authority:\n{{to_json owner}}\n\nactive permission with authority:\n{{to_json active}}" + }, + { + "name": "onerror", + "type": "onerror", + "ricardian_contract": "" + }, + { + "name": "reqactivated", + "type": "reqactivated", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Assert Protocol Feature Activation\nsummary: 'Assert that protocol feature {{nowrap feature_digest}} has been activated'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/admin.png#9bf1cec664863bd6aaac0f814b235f8799fb02c850e9aa5da34e8a004bd6518e\n---\n\nAssert that the protocol feature with a digest of {{feature_digest}} has been activated." + }, + { + "name": "setabi", + "type": "setabi", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Deploy Contract ABI\nsummary: 'Deploy contract ABI on account {{nowrap account}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\nDeploy the ABI file associated with the contract on account {{account}}." + }, + { + "name": "setcode", + "type": "setcode", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Deploy Contract Code\nsummary: 'Deploy contract code on account {{nowrap account}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\nDeploy compiled contract code to the account {{account}}." + }, + { + "name": "unlinkauth", + "type": "unlinkauth", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Unlink Action from Permission\nsummary: '{{nowrap account}} unsets the minimum required permission for the {{#if type}}{{nowrap type}} action of the{{/if}} {{nowrap code}} contract'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\n{{account}} removes the association between the {{#if type}}{{type}} action of the{{/if}} {{code}} contract and its minimum required permission.\n\n{{#if type}}{{else}}This will not remove any links explicitly associated to specific actions of {{code}}.{{/if}}" + }, + { + "name": "updateauth", + "type": "updateauth", + "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Modify Account Permission\nsummary: 'Add or update the {{nowrap permission}} permission of {{nowrap account}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/account.png#3d55a2fc3a5c20b456f5657faf666bc25ffd06f4836c5e8256f741149b0b294f\n---\n\nModify, and create if necessary, the {{permission}} permission of {{account}} to have a parent permission of {{parent}} and the following authority:\n{{to_json auth}}" + } + ], + "tables": [], + "ricardian_clauses": [], + "variants": [], + "action_results": [] +} \ No newline at end of file diff --git a/antelope_contracts/contracts/eosio.boot/eosio.boot.wasm b/antelope_contracts/contracts/eosio.boot/eosio.boot.wasm new file mode 100755 index 0000000..44cdf94 Binary files /dev/null and b/antelope_contracts/contracts/eosio.boot/eosio.boot.wasm differ diff --git a/antelope_contracts/contracts/stubs/stub_evm_runtime.cpp b/antelope_contracts/contracts/stubs/stub_evm_runtime.cpp index 783f7b4..e1be8ce 100644 --- a/antelope_contracts/contracts/stubs/stub_evm_runtime.cpp +++ b/antelope_contracts/contracts/stubs/stub_evm_runtime.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -7,24 +8,36 @@ namespace stub { typedef std::vector bytes; +struct bridge_message_v0 { + eosio::name receiver; + bytes sender; + eosio::time_point timestamp; + bytes value; + bytes data; + + EOSLIB_SERIALIZE(bridge_message_v0, (receiver)(sender)(timestamp)(value)(data)); +}; + +using bridge_message_t = std::variant; + class [[eosio::contract]] stub_evm_runtime : public contract { using contract::contract; public: [[eosio::action]] void call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit); - [[eosio::action]] void sendbridgemsg(eosio::name receiver, const bytes& sender, const eosio::time_point& timestamp, const bytes& value, const bytes& data); + [[eosio::action]] void sendbridgemsg(const bridge_message_t &message); private: - void onbridgemsg(name receiver, const bytes& sender, const time_point& timestamp, const bytes& value, const bytes& data); + void onbridgemsg(const bridge_message_t &message); using onbridgemsg_action = action_wrapper<"onbridgemsg"_n, &stub_evm_runtime::onbridgemsg>; }; void stub_evm_runtime::call(eosio::name from, const bytes& to, uint128_t value, const bytes& data, uint64_t gas_limit) { } -void stub_evm_runtime::sendbridgemsg(eosio::name receiver, const bytes& sender, const eosio::time_point& timestamp, const bytes& value, const bytes& data) { +void stub_evm_runtime::sendbridgemsg(const bridge_message_t &message) { onbridgemsg_action onbridgemsg_act(eosio::name("eosio.erc2o"), {{get_self(), "active"_n}}); - onbridgemsg_act.send(receiver, sender, timestamp, value, data); + onbridgemsg_act.send(message); } } // namespace stub \ No newline at end of file diff --git a/antelope_contracts/tests/erc20/contracts.hpp.in b/antelope_contracts/tests/erc20/contracts.hpp.in index 7016b20..769ac6e 100644 --- a/antelope_contracts/tests/erc20/contracts.hpp.in +++ b/antelope_contracts/tests/erc20/contracts.hpp.in @@ -20,6 +20,10 @@ namespace eosio { namespace testing { struct contracts { + + static std::vector eosio_boot_wasm() { return read_wasm("${ANTELOPE_CONTRACTS_SOURCE_DIR}/eosio.boot/eosio.boot.wasm"); } + static std::vector eosio_boot_abi() { return read_abi("${ANTELOPE_CONTRACTS_SOURCE_DIR}/eosio.boot/eosio.boot.abi"); } + static std::vector eosio_token_wasm() { return read_wasm("${ANTELOPE_CONTRACTS_SOURCE_DIR}/eosio.token/eosio.token.wasm"); } static std::vector eosio_token_abi() { return read_abi("${ANTELOPE_CONTRACTS_SOURCE_DIR}/eosio.token/eosio.token.abi"); } diff --git a/antelope_contracts/tests/erc20/erc20_tester.cpp b/antelope_contracts/tests/erc20/erc20_tester.cpp index 19fccee..d0a3f3b 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.cpp +++ b/antelope_contracts/tests/erc20/erc20_tester.cpp @@ -18,6 +18,7 @@ using mvo = fc::mutable_variant_object; using intx::operator""_u256; namespace erc20_test { +const eosio::chain::name eos_system_account("eosio"); const eosio::chain::name eos_token_account("eosio.token"); const eosio::chain::symbol eos_token_symbol(4u, "EOS"); const eosio::chain::name token_account("tethertether"); @@ -27,9 +28,29 @@ const eosio::chain::name faucet_account_name("eosio.faucet"); const eosio::chain::name erc20_account("eosio.erc2o"); erc20_tester::erc20_tester(std::string native_symbol_str) : native_symbol(symbol::from_string(native_symbol_str)) { - create_accounts({eos_token_account, evm_account, token_account, faucet_account_name, erc20_account}); + + auto def_conf = default_config(tempdir); + def_conf.first.max_nonprivileged_inline_action_size = 256 * 1024; + cfg = def_conf.first; + init(def_conf.first, def_conf.second); + + const auto& pfm = control->get_protocol_feature_manager(); + + auto preactivate_feature_digest = pfm.get_builtin_digest(builtin_protocol_feature_t::preactivate_feature); + FC_ASSERT( preactivate_feature_digest, "PREACTIVATE_FEATURE not found" ); + schedule_protocol_features_wo_preactivation( { *preactivate_feature_digest } ); + produce_block(); + set_code( "eosio"_n, testing::contracts::eosio_boot_wasm() ); + set_abi( "eosio"_n, testing::contracts::eosio_boot_abi().data() ); + + preactivate_all_builtin_protocol_features(); + + produce_block(); + + create_accounts({eos_token_account, evm_account, token_account, faucet_account_name, erc20_account}); + set_code(eos_token_account, testing::contracts::eosio_token_wasm()); set_abi(eos_token_account, testing::contracts::eosio_token_abi().data()); @@ -63,12 +84,11 @@ erc20_tester::erc20_tester(std::string native_symbol_str) : native_symbol(symbol produce_block(); // ./cleos push action eosio.erc2o init '[0]' -p eosio.erc2o - push_action(erc20_account, "init"_n, erc20_account, mvo()("nonce", 0)); + push_action(erc20_account, "upgrade"_n, erc20_account, mvo()); produce_block(); - // ./cleos push action eosio.erc2o regtoken '[1,"usdt","EVM USDT Token","WUSDT","0.1000 USDT","0.0100 USDT","4ea3b729669bf6c34f7b80e5d6c17db71f89f21f",6]' -p eosio.erc2o - push_action(erc20_account, "regtoken"_n, erc20_account, mvo()("nonce", 1)("eos_contract_name",token_account.to_string())("evm_token_name","EVM USDT V1")("evm_token_symbol","WUSDT")("min_deposit","0.1000 USDT")("deposit_fee","0.0100 USDT")("erc20_impl_address","4ea3b729669bf6c34f7b80e5d6c17db71f89f21f")("erc20_precision",6)); + push_action(erc20_account, "regtoken"_n, erc20_account, mvo()("eos_contract_name",token_account.to_string())("evm_token_name","EVM USDT V1")("evm_token_symbol","WUSDT")("ingress_fee","0.0100 USDT")("egress_fee","0.0100 EOS")("erc20_precision",6)); produce_block(); diff --git a/antelope_contracts/tests/erc20/erc20_tester.hpp b/antelope_contracts/tests/erc20/erc20_tester.hpp index 04ef206..864f375 100644 --- a/antelope_contracts/tests/erc20/erc20_tester.hpp +++ b/antelope_contracts/tests/erc20/erc20_tester.hpp @@ -22,7 +22,9 @@ extern const eosio::chain::name erc20_account; using namespace eosio; using namespace eosio::chain; -class erc20_tester : public eosio::testing::validating_tester { +class erc20_tester : public eosio::testing::base_tester { + +private: public: const eosio::chain::symbol native_symbol; explicit erc20_tester(std::string native_symbol_str = "4,EOS"); @@ -38,6 +40,21 @@ class erc20_tester : public eosio::testing::validating_tester { std::vector data = get_row_by_account(token_addr, act, "accounts"_n, name(target_symbol.to_symbol_code().value)); return data.empty() ? eosio::chain::asset(0, target_symbol) : token_abi_ser.binary_to_variant("account", data, eosio::chain::abi_serializer::create_yield_function(abi_serializer_max_time))["balance"].as(); } + + using base_tester::produce_block; + + signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override { + return _produce_block(skip_time, false); + } + + signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override { + unapplied_transactions.add_aborted( control->abort_block() ); + return _produce_block(skip_time, true); + } + + signed_block_ptr finish_block()override { + return _finish_block(); + } }; // Hex helper functions: diff --git a/antelope_contracts/tests/erc20/transfer_tests.cpp b/antelope_contracts/tests/erc20/transfer_tests.cpp index 985a059..409d452 100644 --- a/antelope_contracts/tests/erc20/transfer_tests.cpp +++ b/antelope_contracts/tests/erc20/transfer_tests.cpp @@ -44,13 +44,55 @@ struct transfer_tester : erc20_tester { calldata.insert(calldata.end(), value_buffer, value_buffer + 32); // Cannot directly send message to erc20 + + fc::variants arr; + arr.push_back(fc::variant("bridge_message_v0")); + arr.push_back(mvo() + ("receiver", erc20_account.to_string()) + ("sender", "4ea3b729669bf6c34f7b80e5d6c17db71f89f21f") // contract addr at nonce 0 + ("timestamp","2020-01-01T00:00:00.000000") + ("value", "0000000000000000000000000000000000000000000000000000000000000000") + ("data", + "653332e5" //sha("bridgeTransferV0(address,uint256,string)") + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000" // bob- dest account + "0000000000000000000000000000000000000000000000000000000000001388" // hex(5000)-ERC-20 val + "0000000000000000000000000000000000000000000000000000000000000060" // hex(96)-memo offset + "0000000000000000000000000000000000000000000000000000000000000004" // memo len + "aabbccdd00000000000000000000000000000000000000000000000000000000" // memo data aligned to 32 bytes + )); + + fc::variants arr_hacker_solidity_contract; + arr_hacker_solidity_contract.push_back(fc::variant("bridge_message_v0")); + arr_hacker_solidity_contract.push_back(mvo() + ("receiver", erc20_account.to_string()) + ("sender", "aaaabbbbccccdddd111122223333444455556666") // hacker's solidity contract + ("timestamp","2020-01-01T00:00:00.000000") + ("value", "0000000000000000000000000000000000000000000000000000000000000000") + ("data", + "653332e5" //sha("bridgeTransferV0(address,uint256,string)") + "000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000" // bob- dest account + "0000000000000000000000000000000000000000000000000000000000001388" // hex(5000)-ERC-20 val + "0000000000000000000000000000000000000000000000000000000000000060" // hex(96)-memo offset + "0000000000000000000000000000000000000000000000000000000000000004" // memo len + "aabbccdd00000000000000000000000000000000000000000000000000000000" // memo data aligned to 32 bytes + )); + + // hacker can't trigger the bridge from other solidity contract + BOOST_REQUIRE_EXCEPTION( + push_action( + evm_account, "sendbridgemsg"_n, evm_account, mvo()("message", arr_hacker_solidity_contract)), + eosio_assert_message_exception, + eosio_assert_message_is("ERC-20 token not registerred")); + + // only evm_runtime can call onbridgemsg BOOST_REQUIRE_EXCEPTION(push_action( - erc20_account, "onbridgemsg"_n, "alice"_n, mvo()("receiver", erc20_account)("sender", bytes())("timestamp", eosio::chain::time_point())("value", bytes())("data", calldata)), - missing_auth_exception, eosio::testing::fc_exception_message_starts_with("missing authority")); + erc20_account, "onbridgemsg"_n, "alice"_n, mvo()("message", arr)), + eosio_assert_message_exception, + eosio_assert_message_is("invalid sender of onbridgemsg")); // Go through stub push_action( - evm_account, "sendbridgemsg"_n, "alice"_n, mvo()("receiver", erc20_account)("sender", bytes())("timestamp", eosio::chain::time_point())("value", bytes())("data", calldata)); + evm_account, "sendbridgemsg"_n, evm_account, mvo()("message", arr)); } }; @@ -79,7 +121,7 @@ try { BOOST_REQUIRE(trace->action_traces.back().act.name.to_string() == "call"); BOOST_REQUIRE_EXCEPTION(transfer_token(eos_token_account, "alice"_n, erc20_account, make_asset(10000), "0x0000000000000000000000000000000000000000"), - eosio_assert_message_exception, eosio_assert_message_is("received unexpected token")); + eosio_assert_message_exception, eosio_assert_message_is("received unregistered token")); produce_block(); } @@ -103,7 +145,7 @@ try { const char bob[] = "0xbbbbbbbbbbbbbbbbbbbbbbbb3d0e000000000000"; // EVM has precision of 6 - // 5000 /1000000 = 0.005 EOS = 50 + // 5000 /1000000 = 0.005 USDT = 50 gen_bridgemessage(bob, 5000); BOOST_REQUIRE(10000 - 50 == get_balance(erc20_account, token_account, symbol::from_string("4,USDT")).get_amount());