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 #152 from nervosnetwork/fix-calculate_fee
Browse files Browse the repository at this point in the history
Fix the fee calculation
  • Loading branch information
Flouse authored May 11, 2022
2 parents ffb4b97 + b66a77b commit 2607851
Show file tree
Hide file tree
Showing 14 changed files with 196 additions and 29 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:7b168b4b109a0f741078a71b
# TODO: update to nervos/ckb-riscv-gnu-toolchain:bionic-20211214 => need more tests

all: build/blockchain.h build/godwoken.h \
build/test_contracts build/test_rlp build/test_ripemd160 \
build/test_contracts build/test_rlp build/test_ripemd160 build/test_calc_fee \
build/generator build/validator \
build/generator_log build/validator_log

Expand Down Expand Up @@ -150,6 +150,9 @@ build/test_rlp: c/tests/test_rlp.c $(VALIDATOR_DEPS)
$(OBJCOPY) --strip-debug --strip-all $@
cd $(SECP_DIR) && (git apply -R workaround-fix-g++-linking.patch || true) && cd - # revert patch

build/test_calc_fee: c/tests/test_calc_fee.c $(VALIDATOR_DEPS)
$(CXX) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ c/tests/test_calc_fee.c $(ALL_OBJS)

build/test_ripemd160: c/ripemd160/test_ripemd160.c c/ripemd160/ripemd160.h c/ripemd160/memzero.h $(ALL_OBJS)
$(CXX) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ c/ripemd160/test_ripemd160.c $(ALL_OBJS)
riscv64-unknown-elf-run build/test_ripemd160
Expand Down
25 changes: 6 additions & 19 deletions c/polyjuice.h
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,10 @@ int run_polyjuice() {
return clean_evmc_result_and_return(&res, ret_handle_message);
}

uint32_t used_memory;
memcpy(&used_memory, res.padding, sizeof(uint32_t));
debug_print_int("[run_polyjuice] used_memory(Bytes)", used_memory);

/* Handle transaction fee */
if (res.gas_left < 0) {
ckb_debug("gas not enough");
Expand All @@ -1361,36 +1365,19 @@ int run_polyjuice() {
ckb_debug("unreachable!");
return clean_evmc_result_and_return(&res, -1);
}
uint128_t fee = gas_price * (uint128_t)gas_used;

debug_print_int("gas limit", msg.gas);
debug_print_int("gas left", res.gas_left);
debug_print_int("gas price", gas_price);
debug_print_int("fee", fee);

uint32_t used_memory;
memcpy(&used_memory, res.padding, sizeof(uint32_t));
debug_print_int("[run_polyjuice] used_memory(Bytes)", used_memory);
uint256_t fee_u256 = calculate_fee(gas_price, gas_used);

gw_reg_addr_t sender_addr = new_reg_addr(msg.sender.bytes);

uint256_t fee_u256 = {0};
memcpy((uint8_t *)(&fee_u256), (uint8_t*)(&fee), 16);

ret = sudt_pay_fee(&context, g_sudt_id, /* g_sudt_id must already exists */
sender_addr, fee_u256);
if (ret != 0) {
debug_print_int("[run_polyjuice] pay fee to block_producer failed", ret);
return clean_evmc_result_and_return(&res, ret);
}

// call the SYS_PAY_FEE syscall to record the fee
// NOTICE: this function do not actually execute the transfer of assets
ret = sys_pay_fee(&context, sender_addr, g_sudt_id, fee_u256);
if (ret != 0) {
debug_print_int("[run_polyjuice] Record fee payment failed", ret);
return clean_evmc_result_and_return(&res, ret);
}

ckb_debug("[run_polyjuice] finalize");
ret = gw_finalize(&context);
if (ret != 0) {
Expand Down
2 changes: 1 addition & 1 deletion c/polyjuice_globals.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef POLYJUICE_GLOBALS_H
#define POLYJUICE_GLOBALS_H

#define POLYJUICE_VERSION "v1.1.4-beta"
#define POLYJUICE_VERSION "v1.1.5-beta"

#define ETH_ADDRESS_LEN 20

Expand Down
30 changes: 30 additions & 0 deletions c/polyjuice_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,36 @@ int parse_u256(const uint8_t data_be[32], uint256_t *value) {
return parse_integer(data_be, (uint8_t *)value, sizeof(uint256_t));
}

uint128_t hi(uint128_t x) {
return x >> 64;
}

uint128_t lo(uint128_t x) {
return 0xFFFFFFFFFFFFFFFF & x;
}

/**
* @brief calculate fee => gas_price * gas_used
*
* Multiplication Algorithm
* https://en.wikipedia.org/wiki/Multiplication_algorithm
*
* @param gas_price msg.gas_price
* @param gas_used = msg.gas_limit - res.gas_left
* @return uint256_t
*/
uint256_t calculate_fee(uint128_t gas_price, uint64_t gas_used) {
uint128_t price_high = hi(gas_price);
uint128_t price_low = lo(gas_price);
uint128_t fee_low = price_low * gas_used;
uint128_t fee_high = hi(fee_low) + price_high * gas_used;

uint256_t fee_u256 = {0};
_gw_fast_memcpy((uint8_t *)(&fee_u256), (uint8_t*)(&fee_low), 8);
_gw_fast_memcpy((uint8_t *)(&fee_u256) + 8, (uint8_t*)(&fee_high), 16);
return fee_u256;
}

/* serialize uint64_t to big endian byte32 */
void put_u64(uint64_t value, uint8_t *output) {
uint8_t *value_le = (uint8_t *)(&value);
Expand Down
48 changes: 48 additions & 0 deletions c/tests/test_calc_fee.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#include "test_utils.h"
#include "gw_def.h"
#include "generator_utils.h"
#include "../polyjuice_utils.h"
#include <assert.h>

void test(uint128_t gas_price, uint64_t gas_used, uint256_t expected_fee) {
uint256_t result = calculate_fee(gas_price, gas_used);
assert(gw_uint256_cmp(result, expected_fee) == GW_UINT256_EQUAL);
}

int main() {
uint256_t expected_result = {0};
gw_uint256_cmp(expected_result, expected_result);
test(0, 0, expected_result);
test(0, 1, expected_result);
test(1, 0, expected_result);

gw_uint256_one(&expected_result);
test(1, 1, expected_result);

uint128_t gas_price = 11;
expected_result.array[0] = 22;
test(gas_price, 2, expected_result);

gas_price = 0xfedbca9876543210ULL;
expected_result.array[0] = 0x76543210;
expected_result.array[1] = 0xfedbca98;
test(gas_price, 1, expected_result);
test(gas_price, 2, {0xECA86420UL, 0xFDB79530UL, 0x1UL});

gas_price = ((uint128_t)0xF0F0F0F0F0F0F0F0 << 64) | 0xF0F0F0F0F0F0F0F0;
gw_uint256_zero(&expected_result);
test(gas_price, 0, expected_result);
test(gas_price, 1, {0xF0F0F0F0UL, 0xF0F0F0F0UL, 0xF0F0F0F0UL, 0xF0F0F0F0UL});

uint64_t gas_used = 0xaaaaaaaaaaaaaaaaULL;
test(gas_price, gas_used, {0x5f5f5f60, 0x5f5f5f5f, 0xffffffff, 0xffffffff,
0xA0A0A09F, 0xA0A0A0A0, 0x00000000, 0x00000000});

const uint64_t MAX_UINT64 = 0xFFFFFFFFFFFFFFFF;
gas_used = MAX_UINT64;
gas_price = ((uint128_t)MAX_UINT64 << 64) | MAX_UINT64;
test(gas_price, gas_used, {0x00000001, 0x00000000, 0xffffffff, 0xffffffff,
0xFFFFFFFE, 0xFFFFFFFF, 0x00000000, 0x00000000});

return 0;
}
2 changes: 1 addition & 1 deletion polyjuice-tests/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion polyjuice-tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "polyjuice-tests"
version = "1.1.0"
version = "1.1.5"
authors = ["Linfeng Qian <thewawar@gmail.com>"]
edition = "2018"

Expand Down
2 changes: 1 addition & 1 deletion polyjuice-tests/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ fn parse_sudt_log_data(data: &[u8]) -> (RegistryAddress, RegistryAddress, U256)

let mut u256_bytes = [0u8; 32];
u256_bytes.copy_from_slice(&data[56..56 + 32]);
let amount = U256::from_little_endian(&mut u256_bytes);
let amount = U256::from_little_endian(&u256_bytes);
(from_addr, to_addr, amount)
}

Expand Down
3 changes: 2 additions & 1 deletion polyjuice-tests/src/test_cases/beacon_proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use gw_store::{chain_view::ChainView, traits::chain_store::ChainStore};
use gw_types::{
bytes::Bytes,
packed::RawL2Transaction,
prelude::{Builder, Entity, Pack}, U256,
prelude::{Builder, Entity, Pack},
U256,
};

const CONTRACT_CODE: &str = include_str!("./evm-contracts/BeaconProxy.bin");
Expand Down
7 changes: 5 additions & 2 deletions polyjuice-tests/src/test_cases/delegatecall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::helper::{
new_contract_account_script_with_nonce, setup, simple_storage_get, MockContractInfo,
PolyjuiceArgsBuilder, CREATOR_ACCOUNT_ID, L2TX_MAX_CYCLES,
};
use gw_common::{state::State, builtins::CKB_SUDT_ACCOUNT_ID};
use gw_common::{builtins::CKB_SUDT_ACCOUNT_ID, state::State};
use gw_generator::traits::StateExt;
use gw_store::chain_view::ChainView;
use gw_store::traits::chain_store::ChainStore;
Expand Down Expand Up @@ -159,7 +159,10 @@ fn test_delegatecall() {
let delegate_contract_balance = state
.get_sudt_balance(CKB_SUDT_ACCOUNT_ID, &delegate_contract.reg_addr)
.unwrap();
assert_eq!(delegate_contract_balance, gw_types::U256::from(MSG_VALUE * 3));
assert_eq!(
delegate_contract_balance,
gw_types::U256::from(MSG_VALUE * 3)
);
assert_eq!(state.get_nonce(from_id).unwrap(), 5);
assert_eq!(state.get_nonce(ss_account_id).unwrap(), 0);
assert_eq!(state.get_nonce(delegate_contract_id).unwrap(), 0);
Expand Down
2 changes: 1 addition & 1 deletion polyjuice-tests/src/test_cases/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ fn test_error_handling() {

// testRevertMsg("test revert message") -> 0x5729f42c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000137465737420726576657274206d65737361676500000000000000000000000000
block_number += 1;
let block_info = helper::new_block_info(block_producer.to_owned(), block_number, block_number);
let block_info = helper::new_block_info(block_producer, block_number, block_number);
let input =
hex::decode("5729f42c000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000137465737420726576657274206d65737361676500000000000000000000000000")
.expect("decode the function selector and arg of testRevertMsg('test revert message')");
Expand Down
1 change: 1 addition & 0 deletions polyjuice-tests/src/test_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ pub(crate) mod sudt_erc20_proxy;
mod beacon_proxy;
mod error;
mod eth_addr_reg;
mod utils;
2 changes: 1 addition & 1 deletion polyjuice-tests/src/test_cases/simple_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn test_simple_transfer() {
let mint_balance = U256::from(400000u128);
let from_eth_address = [1u8; 20];
let (from_id, _from_script_hash) =
helper::create_eth_eoa_account(&mut state, &from_eth_address, mint_balance.into());
helper::create_eth_eoa_account(&mut state, &from_eth_address, mint_balance);
let from_addr = RegistryAddress::new(ETH_REGISTRY_ACCOUNT_ID, from_eth_address.to_vec());

let target_eth_addr = [2u8; 20];
Expand Down
94 changes: 94 additions & 0 deletions polyjuice-tests/src/test_cases/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::helper::L2TX_MAX_CYCLES;
use ckb_vm::{
machine::asm::{AsmCoreMachine, AsmMachine},
memory::Memory,
registers::{A0, A7},
DefaultMachineBuilder, Error as VMError, Register, SupportMachine, Syscalls,
};
use gw_types::bytes::Bytes;

const BINARY: &[u8] = include_bytes!("../../../build/test_calc_fee");
const DEBUG_PRINT_SYSCALL_NUMBER: u64 = 2177;

pub struct L2Syscalls;

impl<Mac: SupportMachine> Syscalls<Mac> for L2Syscalls {
fn initialize(&mut self, _machine: &mut Mac) -> Result<(), VMError> {
Ok(())
}

fn ecall(&mut self, machine: &mut Mac) -> Result<bool, VMError> {
let code = machine.registers()[A7].to_u64();
if code != DEBUG_PRINT_SYSCALL_NUMBER {
println!("code: {}", code);
}
match code {
DEBUG_PRINT_SYSCALL_NUMBER => {
self.output_debug(machine)?;
Ok(true)
}
_ => Ok(false),
}
}
}

impl L2Syscalls {
fn output_debug<Mac: SupportMachine>(&self, machine: &mut Mac) -> Result<(), VMError> {
let mut addr = machine.registers()[A0].to_u64();
let mut buffer = Vec::new();

loop {
let byte = machine
.memory_mut()
.load8(&Mac::REG::from_u64(addr))?
.to_u8();
if byte == 0 {
break;
}
buffer.push(byte);
addr += 1;
}

let s = String::from_utf8(buffer).map_err(|_| VMError::ParseError)?;
println!("[debug]: {}", s);
Ok(())
}
}

// TODO: refactor
struct AsmCoreMachineParams {
pub vm_isa: u8,
pub vm_version: u32,
}

impl AsmCoreMachineParams {
pub fn with_version(vm_version: u32) -> Result<AsmCoreMachineParams, VMError> {
if vm_version == 0 {
Ok(AsmCoreMachineParams {
vm_isa: ckb_vm::ISA_IMC,
vm_version: ckb_vm::machine::VERSION0,
})
} else if vm_version == 1 {
Ok(AsmCoreMachineParams {
vm_isa: ckb_vm::ISA_IMC | ckb_vm::ISA_B | ckb_vm::ISA_MOP,
vm_version: ckb_vm::machine::VERSION1,
})
} else {
Err(VMError::InvalidVersion)
}
}
}

#[test]
fn test_calc_fee() {
let binary: Bytes = BINARY.to_vec().into();

let params = AsmCoreMachineParams::with_version(1).unwrap();
let core_machine = AsmCoreMachine::new(params.vm_isa, params.vm_version, L2TX_MAX_CYCLES);
let machine_builder = DefaultMachineBuilder::new(core_machine).syscall(Box::new(L2Syscalls));
let mut machine = AsmMachine::new(machine_builder.build(), None);

machine.load_program(&binary, &[]).unwrap();
let code = machine.run().unwrap();
assert_eq!(code, 0);
}

0 comments on commit 2607851

Please sign in to comment.