Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Commit

Permalink
Merge pull request #57 from TheWaWaR/fix-call-non-exists-contract
Browse files Browse the repository at this point in the history
fix: allow call non-exists account address
  • Loading branch information
TheWaWaR authored Jul 12, 2021
2 parents adcea71 + ecbeb7c commit 46f0ab7
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 56 deletions.
108 changes: 55 additions & 53 deletions c/polyjuice.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ int load_account_code(gw_context_t* gw_ctx, uint32_t account_id,
if (raw_args_seg.size != CONTRACT_ACCOUNT_SCRIPT_ARGS_SIZE) {
debug_print_int("invalid account script", account_id);
debug_print_int("raw_args_seg.size", raw_args_seg.size);
return -1;
// This is an EoA or other kind of account
*code_size = 0;
return 0;
}
if (memcmp(code_hash_seg.ptr, g_script_code_hash, 32) != 0
|| *hash_type_seg.ptr != g_script_hash_type
Expand All @@ -256,7 +258,9 @@ int load_account_code(gw_context_t* gw_ctx, uint32_t account_id,
|| memcmp(&g_creator_account_id, raw_args_seg.ptr + 32, sizeof(uint32_t)) != 0
) {
debug_print_int("creator account id not match for account", account_id);
return -1;
// This is an EoA or other kind of account
*code_size = 0;
return 0;
}

debug_print_int("load_account_code, account_id:", account_id);
Expand Down Expand Up @@ -294,7 +298,7 @@ int load_account_code(gw_context_t* gw_ctx, uint32_t account_id,
return ret;
}
if (*code_size > old_code_size) {
ckb_debug("code can't be larger than MAX_DATA_SIZE");
debug_print_int("code can't be larger than", MAX_DATA_SIZE);
return -1;
}
return 0;
Expand Down Expand Up @@ -392,7 +396,6 @@ size_t get_code_size(struct evmc_host_context* context,
ret = address_to_account_id(context->gw_ctx, address->bytes, &account_id);
if (ret != 0) {
ckb_debug("get contract account id failed");
context->error_code = ret;
return 0;
}
uint8_t code[MAX_DATA_SIZE];
Expand Down Expand Up @@ -751,6 +754,11 @@ int create_new_account(gw_context_t* ctx,
size_t code_size) {
static const uint32_t SCRIPT_ARGS_LEN = 32 + 4 + 20;

if (code_size == 0) {
ckb_debug("can't create new account by empty code data");
return -1;
}

int ret = 0;
uint8_t script_args[SCRIPT_ARGS_LEN];
uint8_t data[128] = {0};
Expand Down Expand Up @@ -831,7 +839,7 @@ int create_new_account(gw_context_t* ctx,
int handle_transfer(gw_context_t* ctx,
const evmc_message* msg,
uint8_t tx_origin_addr[20],
bool transfer_only) {
bool to_address_is_eoa) {
int ret;
uint8_t value_u128_bytes[16];
for (int i = 0; i < 16; i++) {
Expand All @@ -855,7 +863,7 @@ int handle_transfer(gw_context_t* ctx,
return ret;
}

if (msg->kind == EVMC_CALL && memcmp(msg->sender.bytes, tx_origin_addr, 20) == 0 && transfer_only) {
if (msg->kind == EVMC_CALL && memcmp(msg->sender.bytes, tx_origin_addr, 20) == 0 && to_address_is_eoa) {
ckb_debug("transfer value from eoa to eoa");
return -1;
}
Expand Down Expand Up @@ -932,55 +940,35 @@ int handle_message(gw_context_t* ctx,
uint32_t parent_to_id,
evmc_address *parent_destination,
const evmc_message* msg_origin, struct evmc_result* res) {
ckb_debug("BEGIN handle_message");
static const evmc_address zero_address{0};

ckb_debug("BEGIN handle_message");
evmc_message msg = *msg_origin;

int ret;

/* to_id may still a contract account, but can be treated as EoA account */
bool transfer_only = false;
uint32_t to_id;
bool to_address_exists = false;
uint32_t to_id = 0;
uint32_t from_id;
// TODO: passing this value from function argument
uint8_t from_script_hash[32] = {0};
uint8_t to_script_hash[32] = {0};
if (msg.input_size == 0) {
if (is_create(msg.kind)) {
ckb_debug("CREATE/CREATE2 must have input data");
return -1;
}

if (memcmp(zero_address.bytes, msg.destination.bytes, 20) != 0) {
ret = ctx->sys_get_script_hash_by_prefix(ctx, msg.destination.bytes, 20, to_script_hash);
if (ret == 0) {
to_address_exists = true;
ret = ctx->sys_get_account_id_by_script_hash(ctx, to_script_hash, &to_id);
if (ret != 0) {
debug_print_data("get to account id by script hash failed", to_script_hash, 32);
return -1;
}
} else {
// NOTE: Both to_id and to_script_hash are undefined (can't be used)
ckb_debug("to id will be treated as EoA");
transfer_only = true;
}
} else {
static const evmc_address zero_address{0};
if (memcmp(zero_address.bytes, msg.destination.bytes, 20) == 0) {
to_id = 0;
} else {
ret = ctx->sys_get_script_hash_by_prefix(ctx, msg.destination.bytes, 20, to_script_hash);
if (ret == 0) {
ret = ctx->sys_get_account_id_by_script_hash(ctx, to_script_hash, &to_id);
if (ret != 0) {
debug_print_data("get to account id by script hash failed", from_script_hash, 32);
return -1;
}
} else {
debug_print_data("get destination script hash failed", msg.destination.bytes, 20);
return -1;
}
}
/* When msg.destination is zero
1. if is_create(msg.kind) == true, we will run msg.input_data as code in EVM
2. if is_create(msg.kind) == false, code_size must be zero, so it's simply a transfer action
*/
}

ret = ctx->sys_get_script_hash_by_prefix(ctx, msg.sender.bytes, 20, from_script_hash);
if (ret == 0) {
ret = ctx->sys_get_account_id_by_script_hash(ctx, from_script_hash, &from_id);
Expand All @@ -1002,7 +990,7 @@ int handle_message(gw_context_t* ctx,
}

/* Check if target contract is destructed */
if (!is_create(msg.kind) && !transfer_only) {
if (!is_create(msg.kind) && to_address_exists) {
ret = check_destructed(ctx, to_id);
if (ret != 0) {
return ret;
Expand All @@ -1020,20 +1008,21 @@ int handle_message(gw_context_t* ctx,
code_size = msg.input_size;
msg.input_data = NULL;
msg.input_size = 0;
} else {
} else if (to_address_exists) {
/* call kind: CALL/CALLCODE/DELEGATECALL */
if (msg.input_size > 0) {
ret = load_account_code(ctx, to_id, &code_size_u32, 0, code_data_buffer);
if (ret != 0) {
return ret;
}
if (code_size_u32 == 0) {
debug_print_int("empty contract code for account", to_id);
return -1;
}
ret = load_account_code(ctx, to_id, &code_size_u32, 0, code_data_buffer);
if (ret != 0) {
return ret;
}
if (code_size_u32 == 0) {
debug_print_int("empty contract code for account (EoA account)", to_id);
code_data = NULL;
} else {
code_data = code_data_buffer;
code_size = (size_t)code_size_u32;
}
code_size = (size_t)code_size_u32;
} else {
// Call non-exists address
}

/* Handle special call: CALLCODE/DELEGATECALL */
Expand All @@ -1054,6 +1043,7 @@ int handle_message(gw_context_t* ctx,
if (ret != 0) {
return ret;
}
to_address_exists = true;

/* It's a creation polyjuice transaction */
if (parent_from_id == UINT32_MAX && parent_to_id == UINT32_MAX) {
Expand All @@ -1064,15 +1054,24 @@ int handle_message(gw_context_t* ctx,

/* Handle transfer logic.
NOTE: MUST do this before vm.execute and after to_id finalized */
ret = handle_transfer(ctx, &msg, (uint8_t *)g_tx_origin.bytes, transfer_only);
bool to_address_is_eoa = !to_address_exists || (to_address_exists && code_size == 0);
ret = handle_transfer(ctx, &msg, (uint8_t *)g_tx_origin.bytes, to_address_is_eoa);
if (ret != 0) {
return ret;
}

/* NOTE: msg and res are updated */
ret = execute_in_evmone(ctx, &msg, parent_from_id, from_id, to_id, code_data, code_size, res);
if (ret != 0) {
return ret;
if (to_address_exists && code_size > 0 && (is_create(msg.kind) || msg.input_size > 0)) {
ret = execute_in_evmone(ctx, &msg, parent_from_id, from_id, to_id, code_data, code_size, res);
if (ret != 0) {
return ret;
}
} else {
ckb_debug("Don't run evm and return empty data");
res->output_data = NULL;
res->output_size = 0;
res->gas_left = msg.gas;
res->status_code = EVMC_SUCCESS;
}

/* Store contract code though syscall */
Expand All @@ -1091,6 +1090,7 @@ int handle_message(gw_context_t* ctx,

debug_print_data("output data", res->output_data, res->output_size);
debug_print_int("output size", res->output_size);
debug_print_int("gas left", res->gas_left);
debug_print_int("status_code", res->status_code);
ckb_debug("END handle_message");
return (int)res->status_code;
Expand Down Expand Up @@ -1200,6 +1200,8 @@ int run_polyjuice() {
return clean_evmc_result_and_return(&res, -1);
}
if (msg.gas < res.gas_left) {
debug_print_int("msg.gas", msg.gas);
debug_print_int("res.gas_left", res.gas_left);
ckb_debug("unreachable!");
return clean_evmc_result_and_return(&res, -1);
}
Expand Down
76 changes: 76 additions & 0 deletions polyjuice-tests/src/test_cases/contract_call_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use gw_types::{bytes::Bytes, packed::RawL2Transaction, prelude::*};

const SS_INIT_CODE: &str = include_str!("./evm-contracts/SimpleStorage.bin");
const INIT_CODE: &str = include_str!("./evm-contracts/CallContract.bin");
const CALL_NON_EXISTS_INIT_CODE: &str = include_str!("./evm-contracts/CallNonExistsContract.bin");

#[test]
fn test_contract_call_contract() {
Expand Down Expand Up @@ -141,3 +142,78 @@ fn test_contract_call_contract() {
assert_eq!(state.get_nonce(ss_account_id).unwrap(), 0);
assert_eq!(state.get_nonce(new_account_id).unwrap(), 0);
}

#[test]
fn test_contract_call_non_exists_contract() {
let (store, mut state, generator, creator_account_id) = setup();
let block_producer_script = build_eth_l2_script([0x99u8; 20]);
let block_producer_id = state
.create_account_from_script(block_producer_script)
.unwrap();

let from_script = build_eth_l2_script([1u8; 20]);
let from_script_hash = from_script.hash();
let from_short_address = &from_script_hash[0..20];
let from_id = state.create_account_from_script(from_script).unwrap();
state
.mint_sudt(CKB_SUDT_ACCOUNT_ID, from_short_address, 200000)
.unwrap();
let block_number = 1;

// Deploy CallNonExistsContract
let _run_result = deploy(
&generator,
&store,
&mut state,
creator_account_id,
from_id,
CALL_NON_EXISTS_INIT_CODE,
122000,
0,
block_producer_id,
block_number,
);

let contract_account_script =
new_account_script(&mut state, creator_account_id, from_id, false);
let new_account_id = state
.get_account_id_by_script_hash(&contract_account_script.hash().into())
.unwrap()
.unwrap();
{
// Call CallNonExistsContract.rawCall(addr)
let block_info = new_block_info(0, block_number, block_number);
let input =
hex::decode("56c94e70000000000000000000000000000000000fffffffffffffffffffffffffffffff")
.unwrap();
let args = PolyjuiceArgsBuilder::default()
.gas_limit(51000)
.gas_price(1)
.value(0)
.input(&input)
.build();
let raw_tx = RawL2Transaction::new_builder()
.from_id(from_id.pack())
.to_id(new_account_id.pack())
.args(Bytes::from(args).pack())
.build();
let db = store.begin_transaction();
let tip_block_hash = store.get_tip_block_hash().unwrap();
let run_result = generator
.execute_transaction(
&ChainView::new(&db, tip_block_hash),
&state,
&block_info,
&raw_tx,
)
.expect("construct");
assert_eq!(
run_result.return_data,
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
]
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"rawCall","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"}]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
60806040523480156100115760006000fd5b50610017565b610413806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c806356c94e701461003657610030565b60006000fd5b610050600480360381019061004b919061020c565b610066565b60405161005d91906102f5565b60405180910390f35b606060008273ffffffffffffffffffffffffffffffffffffffff1660405161008d906102df565b6000604051808303816000865af19150503d80600081146100ca576040519150601f19603f3d011682016040523d82523d6000602084013e6100cf565b606091505b505090508015156100e05760006000fd5b600060008473ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527ff85396d7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161018b91906102c7565b6000604051808303816000865af19150503d80600081146101c8576040519150601f19603f3d011682016040523d82523d6000602084013e6101cd565b606091505b50915091508115156101df5760006000fd5b8093505050506101ed565050505b919050566103dc565b600081359050610205816103c1565b5b92915050565b60006020828403121561021f5760006000fd5b600061022d848285016101f6565b9150505b92915050565b600061024282610318565b61024c8185610324565b935061025c818560208601610376565b610265816103ab565b84019150505b92915050565b600061027c82610318565b6102868185610336565b9350610296818560208601610376565b8084019150505b92915050565b60006102b0600083610336565b91506102bb826103bd565b6000820190505b919050565b60006102d38284610271565b91508190505b92915050565b60006102ea826102a3565b91508190505b919050565b6000602082019050818103600083015261030f8184610237565b90505b92915050565b6000815190505b919050565b60008282526020820190505b92915050565b60008190505b92915050565b600061034d82610355565b90505b919050565b600073ffffffffffffffffffffffffffffffffffffffff821690505b919050565b60005b838110156103955780820151818401525b602081019050610379565b838111156103a4576000848401525b505b505050565b6000601f19601f83011690505b919050565b5b50565b6103ca81610342565b811415156103d85760006000fd5b5b50565bfea26469706673582212206065ddbf3ca5e52cdc9bbb8326320af0512411ef223c0f55947c75948c423fe864736f6c63430008020033
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pragma solidity >=0.6.0 <0.9.0;

contract CallNonExistsContract {
function rawCall(address addr) public returns (bytes memory) {
(bool success0, ) = addr.call("");
require(success0);
(bool success1, bytes memory data1) = addr.call(abi.encodeWithSignature("nonExistingFunction()"));
require(success1);
return data1;
}
}
Loading

0 comments on commit 46f0ab7

Please sign in to comment.