From 1c495f1317825d5fc6a52a26c8aedcf8e0301d8f Mon Sep 17 00:00:00 2001 From: yarkin Date: Tue, 30 Jul 2024 15:56:51 +0800 Subject: [PATCH 1/3] Full support for upgrade --- .../contracts/erc20/include/erc20/erc20.hpp | 1 + .../contracts/erc20/src/erc20.cpp | 51 ++++++++++- .../tests/erc20/integrated_tests.cpp | 84 +++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp index 414ecba..16b4b6f 100644 --- a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp +++ b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp @@ -60,6 +60,7 @@ class [[eosio::contract]] erc20 : public contract { [[eosio::action]] void init(eosio::name evm_account, eosio::symbol gas_token_symbol, uint64_t gaslimit, uint64_t init_gaslimit); [[eosio::action]] void setgaslimit(std::optional gaslimit, std::optional init_gaslimit); + [[eosio::action]] void callupgrade(std::string proxy_address); struct [[eosio::table("implcontract")]] impl_contract_t { uint64_t id = 0; diff --git a/antelope_contracts/contracts/erc20/src/erc20.cpp b/antelope_contracts/contracts/erc20/src/erc20.cpp index 1f77ad3..8909c56 100644 --- a/antelope_contracts/contracts/erc20/src/erc20.cpp +++ b/antelope_contracts/contracts/erc20/src/erc20.cpp @@ -84,7 +84,7 @@ void erc20::upgrade() { evmc::address impl_addr = silkworm::create_address(reserved_addr, next_nonce); contract_table.emplace(_self, [&](auto &v) { - v.id = id; + v.id = contract_table.available_primary_key(); v.address.resize(kAddressLength); memcpy(&(v.address[0]), impl_addr.bytes, kAddressLength); }); @@ -100,7 +100,7 @@ void erc20::upgradeto(std::string impl_address) { impl_contract_table_t contract_table(_self, _self.value); contract_table.emplace(_self, [&](auto &v) { - v.id = id; + v.id = contract_table.available_primary_key(); v.address.resize(kAddressLength); memcpy(&(v.address[0]), address_bytes->data(), kAddressLength); }); @@ -477,4 +477,51 @@ inline eosio::name erc20::receiver_account()const { return get_self(); } +void erc20::callupgrade(std::string proxy_address) { + require_auth(get_self()); + + config_t config = get_config(); + + auto address_bytes = from_hex(proxy_address); + eosio::check(!!address_bytes, "token address must be valid 0x EVM address"); + eosio::check(address_bytes->size() == kAddressLength, "invalid length of token address"); + + checksum256 addr_key = make_key(*address_bytes); + token_table_t token_table(_self, _self.value); + auto index = token_table.get_index<"by.address"_n>(); + auto token_table_iter = index.find(addr_key); + + check(token_table_iter != index.end() && token_table_iter->address == address_bytes, "ERC-20 token not registerred"); + + impl_contract_table_t contract_table(_self, _self.value); + eosio::check(contract_table.begin() != contract_table.end(), "no implementaion contract available"); + auto contract_itr = contract_table.end(); + --contract_itr; + + + auto pack_uint32 = [&](bytes &ds, uint32_t val) { + uint8_t val_[32] = {}; + val_[28] = (uint8_t)(val >> 24); + val_[29] = (uint8_t)(val >> 16); + val_[30] = (uint8_t)(val >> 8); + val_[31] = (uint8_t)val; + ds.insert(ds.end(), val_, val_ + sizeof(val_)); + }; + + bytes call_data; + // sha(upgradeTo(address)) == 3659cfe6 + uint8_t func_[4] = {0x36,0x59,0xcf,0xe6}; + call_data.insert(call_data.end(), func_, func_ + sizeof(func_)); + + + call_data.insert(call_data.end(), 32 - kAddressLength, 0); // padding for address offset 0 + call_data.insert(call_data.end(), contract_itr->address.begin(), contract_itr->address.end()); + + bytes value_zero; + value_zero.resize(32, 0); + + evm_runtime::call_action call_act(config.evm_account, {{receiver_account(), "active"_n}}); + call_act.send(receiver_account(), token_table_iter->address, value_zero, call_data, config.evm_gaslimit); +} + } // namespace erc20 \ No newline at end of file diff --git a/antelope_contracts/tests/erc20/integrated_tests.cpp b/antelope_contracts/tests/erc20/integrated_tests.cpp index 515db5b..e865f95 100644 --- a/antelope_contracts/tests/erc20/integrated_tests.cpp +++ b/antelope_contracts/tests/erc20/integrated_tests.cpp @@ -14,6 +14,7 @@ #include #include "erc20_tester.hpp" +#include using namespace eosio; using namespace eosio::chain; @@ -273,6 +274,89 @@ try { } FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE(it_upgrade, it_tester) +try { + + + + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + + // Give evm1 some EOS + transfer_token(eos_token_account, "alice"_n, evm_account, make_asset(1000000, eos_token_symbol), evm1.address_0x().c_str()); + produce_block(); + + + // USDT balance should be zero + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 0); + + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + + produce_block(); + + auto fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + produce_block(); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000); + bal = get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount(); + + BOOST_REQUIRE(99990010 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + + // upgrade + + evm_eoa deployer; + evmc::address impl_addr = silkworm::create_address(deployer.address, deployer.next_nonce); + + transfer_token(eos_token_account, faucet_account_name, evmin_account, make_asset(1000000, eos_token_symbol), deployer.address_0x().c_str()); + + auto txn = prepare_deploy_contract_tx(solidity::erc20::bytecode, sizeof(solidity::erc20::bytecode), 10'000'000); + + deployer.sign(txn); + pushtx(txn); + produce_block(); + + push_action(erc20_account, "upgradeto"_n, erc20_account, mvo()("impl_address",fc::variant(impl_addr).as_string())); + + produce_block(); + + push_action(erc20_account, "callupgrade"_n, erc20_account, mvo()("proxy_address",evm_address.substr(2))); + + produce_block(); + + // Perform some basic tests again + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000 + 990000); + + fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + produce_block(); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000 * 2); + +} +FC_LOG_AND_RETHROW() + BOOST_FIXTURE_TEST_CASE(it_unregtoken, it_tester) try { evm_eoa evm1; From 290b3fd891ded8b846488352c39e71aa4b9196fb Mon Sep 17 00:00:00 2001 From: yarkin Date: Mon, 5 Aug 2024 13:05:31 +0800 Subject: [PATCH 2/3] Add api to call ugrade via token contract and token symbol --- .../contracts/erc20/include/erc20/erc20.hpp | 4 +- .../contracts/erc20/src/erc20.cpp | 25 ++++-- .../tests/erc20/integrated_tests.cpp | 84 ++++++++++++++++++- 3 files changed, 104 insertions(+), 9 deletions(-) diff --git a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp index 16b4b6f..984b77c 100644 --- a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp +++ b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp @@ -60,7 +60,8 @@ class [[eosio::contract]] erc20 : public contract { [[eosio::action]] void init(eosio::name evm_account, eosio::symbol gas_token_symbol, uint64_t gaslimit, uint64_t init_gaslimit); [[eosio::action]] void setgaslimit(std::optional gaslimit, std::optional init_gaslimit); - [[eosio::action]] void callupgrade(std::string proxy_address); + [[eosio::action]] void callupgaddr(std::string proxy_address); + [[eosio::action]] void callupgsym(eosio::name token_contract, eosio::symbol token_symbol); struct [[eosio::table("implcontract")]] impl_contract_t { uint64_t id = 0; @@ -137,6 +138,7 @@ class [[eosio::contract]] erc20 : public contract { uint64_t get_next_nonce(); void handle_erc20_transfer(const token_t &token, eosio::asset quantity, const std::string &memo); + void handle_call_upgrade(const bytes& proxy_address); private: eosio::name receiver_account()const; diff --git a/antelope_contracts/contracts/erc20/src/erc20.cpp b/antelope_contracts/contracts/erc20/src/erc20.cpp index 8909c56..0f70b53 100644 --- a/antelope_contracts/contracts/erc20/src/erc20.cpp +++ b/antelope_contracts/contracts/erc20/src/erc20.cpp @@ -477,11 +477,9 @@ inline eosio::name erc20::receiver_account()const { return get_self(); } -void erc20::callupgrade(std::string proxy_address) { +void erc20::callupgaddr(std::string proxy_address){ require_auth(get_self()); - config_t config = get_config(); - auto address_bytes = from_hex(proxy_address); eosio::check(!!address_bytes, "token address must be valid 0x EVM address"); eosio::check(address_bytes->size() == kAddressLength, "invalid length of token address"); @@ -492,13 +490,28 @@ void erc20::callupgrade(std::string proxy_address) { auto token_table_iter = index.find(addr_key); check(token_table_iter != index.end() && token_table_iter->address == address_bytes, "ERC-20 token not registerred"); + + handle_call_upgrade(token_table_iter->address); +} + +void erc20::callupgsym(eosio::name token_contract, eosio::symbol token_symbol){ + require_auth(get_self()); + + token_table_t token_table(_self, _self.value); + auto index_symbol = token_table.get_index<"by.symbol"_n>(); + auto token_table_iter = index_symbol.find(token_symbol_key(token_contract, token_symbol.code())); + eosio::check(token_table_iter != index_symbol.end(), "token not registered"); + handle_call_upgrade(token_table_iter->address); +} + +void erc20::handle_call_upgrade(const bytes& proxy_address) { + config_t config = get_config(); impl_contract_table_t contract_table(_self, _self.value); eosio::check(contract_table.begin() != contract_table.end(), "no implementaion contract available"); auto contract_itr = contract_table.end(); --contract_itr; - - + auto pack_uint32 = [&](bytes &ds, uint32_t val) { uint8_t val_[32] = {}; val_[28] = (uint8_t)(val >> 24); @@ -521,7 +534,7 @@ void erc20::callupgrade(std::string proxy_address) { value_zero.resize(32, 0); evm_runtime::call_action call_act(config.evm_account, {{receiver_account(), "active"_n}}); - call_act.send(receiver_account(), token_table_iter->address, value_zero, call_data, config.evm_gaslimit); + call_act.send(receiver_account(), proxy_address, value_zero, call_data, config.evm_gaslimit); } } // namespace erc20 \ No newline at end of file diff --git a/antelope_contracts/tests/erc20/integrated_tests.cpp b/antelope_contracts/tests/erc20/integrated_tests.cpp index e865f95..456e57a 100644 --- a/antelope_contracts/tests/erc20/integrated_tests.cpp +++ b/antelope_contracts/tests/erc20/integrated_tests.cpp @@ -274,7 +274,87 @@ try { } FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE(it_upgrade, it_tester) +BOOST_FIXTURE_TEST_CASE(it_upgrade_addr, it_tester) +try { + evm_eoa evm1; + auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); + + // Give evm1 some EOS + transfer_token(eos_token_account, "alice"_n, evm_account, make_asset(1000000, eos_token_symbol), evm1.address_0x().c_str()); + produce_block(); + + + // USDT balance should be zero + auto bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 0); + + produce_block(); + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + bal = balanceOf(evm1.address_0x().c_str()); + BOOST_REQUIRE(bal == 990000); + BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + auto tokenInfo = getRegistedTokenInfo(); + BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); + BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); + + produce_block(); + + auto fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + produce_block(); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000); + bal = get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount(); + + BOOST_REQUIRE(99990010 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); + + // upgrade + + evm_eoa deployer; + evmc::address impl_addr = silkworm::create_address(deployer.address, deployer.next_nonce); + + transfer_token(eos_token_account, faucet_account_name, evmin_account, make_asset(1000000, eos_token_symbol), deployer.address_0x().c_str()); + + auto txn = prepare_deploy_contract_tx(solidity::erc20::bytecode, sizeof(solidity::erc20::bytecode), 10'000'000); + + deployer.sign(txn); + pushtx(txn); + produce_block(); + + push_action(erc20_account, "upgradeto"_n, erc20_account, mvo()("impl_address",fc::variant(impl_addr).as_string())); + + produce_block(); + + push_action(erc20_account, "callupgaddr"_n, erc20_account, mvo()("proxy_address",evm_address.substr(2))); + + produce_block(); + + // Perform some basic tests again + + transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000 + 990000); + + fee = egressFee(); + // received = 1000/1e6*1e4 = 10 + bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); + produce_block(); + + bal = balanceOf(evm1.address_0x().c_str()); + + BOOST_REQUIRE(bal == 989000 * 2); + +} +FC_LOG_AND_RETHROW() + +BOOST_FIXTURE_TEST_CASE(it_upgrade_symbol, it_tester) try { @@ -333,7 +413,7 @@ try { produce_block(); - push_action(erc20_account, "callupgrade"_n, erc20_account, mvo()("proxy_address",evm_address.substr(2))); + push_action(erc20_account, "callupgsym"_n, erc20_account, mvo()("token_contract",token_account)("token_symbol",symbol::from_string("4,USDT"))); produce_block(); From 9778a59ac0b694a8b357d3011de094fa0d42256b Mon Sep 17 00:00:00 2001 From: yarkin Date: Wed, 7 Aug 2024 10:46:37 +0800 Subject: [PATCH 3/3] After discussion, we decide that we should only keep the token address+symbol API --- .../contracts/erc20/include/erc20/erc20.hpp | 3 +- .../contracts/erc20/src/erc20.cpp | 19 +---- .../tests/erc20/integrated_tests.cpp | 84 +------------------ 3 files changed, 4 insertions(+), 102 deletions(-) diff --git a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp index 984b77c..bf711e4 100644 --- a/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp +++ b/antelope_contracts/contracts/erc20/include/erc20/erc20.hpp @@ -60,8 +60,7 @@ class [[eosio::contract]] erc20 : public contract { [[eosio::action]] void init(eosio::name evm_account, eosio::symbol gas_token_symbol, uint64_t gaslimit, uint64_t init_gaslimit); [[eosio::action]] void setgaslimit(std::optional gaslimit, std::optional init_gaslimit); - [[eosio::action]] void callupgaddr(std::string proxy_address); - [[eosio::action]] void callupgsym(eosio::name token_contract, eosio::symbol token_symbol); + [[eosio::action]] void callupgrade(eosio::name token_contract, eosio::symbol token_symbol); struct [[eosio::table("implcontract")]] impl_contract_t { uint64_t id = 0; diff --git a/antelope_contracts/contracts/erc20/src/erc20.cpp b/antelope_contracts/contracts/erc20/src/erc20.cpp index 0f70b53..05bf401 100644 --- a/antelope_contracts/contracts/erc20/src/erc20.cpp +++ b/antelope_contracts/contracts/erc20/src/erc20.cpp @@ -477,24 +477,7 @@ inline eosio::name erc20::receiver_account()const { return get_self(); } -void erc20::callupgaddr(std::string proxy_address){ - require_auth(get_self()); - - auto address_bytes = from_hex(proxy_address); - eosio::check(!!address_bytes, "token address must be valid 0x EVM address"); - eosio::check(address_bytes->size() == kAddressLength, "invalid length of token address"); - - checksum256 addr_key = make_key(*address_bytes); - token_table_t token_table(_self, _self.value); - auto index = token_table.get_index<"by.address"_n>(); - auto token_table_iter = index.find(addr_key); - - check(token_table_iter != index.end() && token_table_iter->address == address_bytes, "ERC-20 token not registerred"); - - handle_call_upgrade(token_table_iter->address); -} - -void erc20::callupgsym(eosio::name token_contract, eosio::symbol token_symbol){ +void erc20::callupgrade(eosio::name token_contract, eosio::symbol token_symbol){ require_auth(get_self()); token_table_t token_table(_self, _self.value); diff --git a/antelope_contracts/tests/erc20/integrated_tests.cpp b/antelope_contracts/tests/erc20/integrated_tests.cpp index 456e57a..fb2e08a 100644 --- a/antelope_contracts/tests/erc20/integrated_tests.cpp +++ b/antelope_contracts/tests/erc20/integrated_tests.cpp @@ -274,87 +274,7 @@ try { } FC_LOG_AND_RETHROW() -BOOST_FIXTURE_TEST_CASE(it_upgrade_addr, it_tester) -try { - evm_eoa evm1; - auto addr_alice = silkworm::make_reserved_address("alice"_n.to_uint64_t()); - - // Give evm1 some EOS - transfer_token(eos_token_account, "alice"_n, evm_account, make_asset(1000000, eos_token_symbol), evm1.address_0x().c_str()); - produce_block(); - - - // USDT balance should be zero - auto bal = balanceOf(evm1.address_0x().c_str()); - BOOST_REQUIRE(bal == 0); - - produce_block(); - - transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); - - bal = balanceOf(evm1.address_0x().c_str()); - BOOST_REQUIRE(bal == 990000); - BOOST_REQUIRE(99990000 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); - auto tokenInfo = getRegistedTokenInfo(); - BOOST_REQUIRE(tokenInfo.balance == make_asset(9900, token_symbol)); - BOOST_REQUIRE(tokenInfo.fee_balance == make_asset(100, token_symbol)); - - produce_block(); - - auto fee = egressFee(); - // received = 1000/1e6*1e4 = 10 - bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); - produce_block(); - - bal = balanceOf(evm1.address_0x().c_str()); - - BOOST_REQUIRE(bal == 989000); - bal = get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount(); - - BOOST_REQUIRE(99990010 == get_balance("alice"_n, token_account, symbol::from_string("4,USDT")).get_amount()); - - // upgrade - - evm_eoa deployer; - evmc::address impl_addr = silkworm::create_address(deployer.address, deployer.next_nonce); - - transfer_token(eos_token_account, faucet_account_name, evmin_account, make_asset(1000000, eos_token_symbol), deployer.address_0x().c_str()); - - auto txn = prepare_deploy_contract_tx(solidity::erc20::bytecode, sizeof(solidity::erc20::bytecode), 10'000'000); - - deployer.sign(txn); - pushtx(txn); - produce_block(); - - push_action(erc20_account, "upgradeto"_n, erc20_account, mvo()("impl_address",fc::variant(impl_addr).as_string())); - - produce_block(); - - push_action(erc20_account, "callupgaddr"_n, erc20_account, mvo()("proxy_address",evm_address.substr(2))); - - produce_block(); - - // Perform some basic tests again - - transfer_token(token_account, "alice"_n, erc20_account, make_asset(10000, token_symbol), evm1.address_0x().c_str()); - - bal = balanceOf(evm1.address_0x().c_str()); - - BOOST_REQUIRE(bal == 989000 + 990000); - - fee = egressFee(); - // received = 1000/1e6*1e4 = 10 - bridgeTransferERC20(evm1, addr_alice, 1000, "aaa", fee); - produce_block(); - - bal = balanceOf(evm1.address_0x().c_str()); - - BOOST_REQUIRE(bal == 989000 * 2); - -} -FC_LOG_AND_RETHROW() - -BOOST_FIXTURE_TEST_CASE(it_upgrade_symbol, it_tester) +BOOST_FIXTURE_TEST_CASE(it_upgrade, it_tester) try { @@ -413,7 +333,7 @@ try { produce_block(); - push_action(erc20_account, "callupgsym"_n, erc20_account, mvo()("token_contract",token_account)("token_symbol",symbol::from_string("4,USDT"))); + push_action(erc20_account, "callupgrade"_n, erc20_account, mvo()("token_contract",token_account)("token_symbol",symbol::from_string("4,USDT"))); produce_block();