Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RPC: Fix and refactor gas fee and fee history RPCs #2788

Merged
merged 18 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/ain-cpp-imports/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub mod ffi {
fn getNetwork() -> String;
fn getEthMaxConnections() -> u32;
fn getEthMaxResponseByteSize() -> u32;
fn getSuggestedPriorityFeePercentile() -> i64;
fn getDifficulty(block_hash: [u8; 32]) -> u32;
fn getChainWork(block_hash: [u8; 32]) -> [u8; 32];
fn getPoolTransactions() -> Vec<TransactionData>;
Expand Down
8 changes: 8 additions & 0 deletions lib/ain-cpp-imports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ mod ffi {
pub fn getEthMaxResponseByteSize() -> u32 {
unimplemented!("{}", UNIMPL_MSG)
}
pub fn getSuggestedPriorityFeePercentile() -> i64 {
unimplemented!("{}", UNIMPL_MSG)
}
pub fn getNetwork() -> String {
unimplemented!("{}", UNIMPL_MSG)
}
Expand Down Expand Up @@ -172,6 +175,11 @@ pub fn get_max_response_byte_size() -> u32 {
ffi::getEthMaxResponseByteSize()
}

/// Gets the suggested priority fee percentile for suggested gas price Ethereum RPC calls.
pub fn get_suggested_priority_fee_percentile() -> i64 {
ffi::getSuggestedPriorityFeePercentile()
}

/// Retrieves the network identifier as a string.
pub fn get_network() -> String {
ffi::getNetwork()
Expand Down
418 changes: 256 additions & 162 deletions lib/ain-evm/src/block.rs

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions lib/ain-grpc/src/rpc/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ pub trait MetachainRPC {
&self,
block_count: U256,
newest_block: BlockNumber,
reward_percentile: Vec<usize>,
reward_percentile: Vec<i64>,
Jouzo marked this conversation as resolved.
Show resolved Hide resolved
) -> RpcResult<RpcFeeHistory>;

#[method(name = "maxPriorityFeePerGas")]
Expand Down Expand Up @@ -898,9 +898,10 @@ impl MetachainRPCServer for MetachainRPCModule {
}

fn gas_price(&self) -> RpcResult<U256> {
let gas_price = self.handler.block.get_legacy_fee().map_err(to_custom_err)?;
debug!(target:"rpc", "gasPrice: {:#?}", gas_price);
Ok(gas_price)
self.handler
.block
.suggest_legacy_fee()
.map_err(to_custom_err)
}

fn get_receipt(&self, hash: H256) -> RpcResult<Option<ReceiptResult>> {
Expand Down Expand Up @@ -931,7 +932,7 @@ impl MetachainRPCServer for MetachainRPCModule {
&self,
block_count: U256,
newest_block: BlockNumber,
reward_percentile: Vec<usize>,
reward_percentile: Vec<i64>,
) -> RpcResult<RpcFeeHistory> {
let highest_block_number = self.get_block(Some(newest_block))?.header.number;
let attrs = ain_cpp_imports::get_attribute_values(None);
Expand All @@ -953,7 +954,7 @@ impl MetachainRPCServer for MetachainRPCModule {
fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
self.handler
.block
.suggested_priority_fee()
.suggest_priority_fee()
.map_err(to_custom_err)
}

Expand Down
6 changes: 1 addition & 5 deletions lib/ain-rs-exports/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ pub fn debug_log_account_states() -> Result<String> {
return Err("debug_* RPCs have not been enabled".into());
}

let backend = SERVICES
.evm
.core
.get_latest_block_backend()
.expect("Error restoring backend");
let backend = SERVICES.evm.core.get_latest_block_backend()?;
let ro_handle = backend.ro_handle();

let mut out = String::new();
Expand Down
8 changes: 4 additions & 4 deletions lib/ain-rs-exports/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ fn evm_try_unsafe_commit_block(template: &BlockTemplateWrapper) -> Result<()> {
#[ffi_fallible]
fn evm_try_disconnect_latest_block() -> Result<()> {
SERVICES.evm.core.clear_account_nonce();
SERVICES.evm.block.clear_last_suggested_fee_tip_cache();
SERVICES.evm.storage.disconnect_latest_block()
}

Expand Down Expand Up @@ -798,12 +799,11 @@ fn evm_try_get_tx_miner_info_from_raw_tx(raw_tx: &str, mnview_ptr: usize) -> Res

let signed_tx = evm_services.core.tx_cache.try_get_or_create(raw_tx)?;

let block_service = &evm_services.block;
let attrs = block_service.get_attribute_vals(Some(mnview_ptr));
let block = &evm_services.block;
let attrs = block.get_attribute_vals(Some(mnview_ptr));

let nonce = u64::try_from(signed_tx.nonce())?;
let initial_base_fee =
block_service.calculate_base_fee(H256::zero(), attrs.block_gas_target_factor)?;
let initial_base_fee = block.calculate_base_fee(H256::zero(), attrs.block_gas_target_factor)?;
let tip_fee = calculate_max_tip_gas_fee(&signed_tx, initial_base_fee)?;
let min_rbf_tip_fee =
calculate_min_rbf_tip_gas_fee(&signed_tx, tip_fee, attrs.rbf_fee_increment)?;
Expand Down
4 changes: 4 additions & 0 deletions src/ffi/ffiexports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ uint32_t getEthMaxResponseByteSize() {
return max_response_size_mb * 1024 * 1024;
}

int64_t getSuggestedPriorityFeePercentile() {
return gArgs.GetArg("-evmtxpriorityfeepercentile", DEFAULT_SUGGESTED_PRIORITY_FEE_PERCENTILE);
}

bool getDST20Tokens(std::size_t mnview_ptr, rust::vec<DST20Token> &tokens) {
LOCK(cs_main);

Expand Down
4 changes: 4 additions & 0 deletions src/ffi/ffiexports.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ static constexpr CAmount DEFAULT_EVM_RBF_FEE_INCREMENT = COIN / 10;
static constexpr uint32_t DEFAULT_ETH_MAX_CONNECTIONS = 100;
static constexpr uint32_t DEFAULT_ETH_MAX_RESPONSE_SIZE_MB = 25; // 25 megabytes

// Defaults for attributes relating to gasprice oracle settings
static constexpr int64_t DEFAULT_SUGGESTED_PRIORITY_FEE_PERCENTILE = 60;

static constexpr uint32_t DEFAULT_ECC_LRU_CACHE_COUNT = 10000;
static constexpr uint32_t DEFAULT_EVMV_LRU_CACHE_COUNT = 10000;

Expand Down Expand Up @@ -70,6 +73,7 @@ rust::string getNetwork();
uint32_t getDifficulty(std::array<uint8_t, 32> blockHash);
uint32_t getEthMaxConnections();
uint32_t getEthMaxResponseByteSize();
int64_t getSuggestedPriorityFeePercentile();
std::array<uint8_t, 32> getChainWork(std::array<uint8_t, 32> blockHash);
rust::vec<TransactionData> getPoolTransactions();
uint64_t getNativeTxSize(rust::Vec<uint8_t> rawTransaction);
Expand Down
3 changes: 2 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ void Shutdown(InitInterfaces& interfaces)
fFeeEstimatesInitialized = false;
}

// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
// generates a ChainStateFlushed callback, which we should avoid missing
//
// g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
// may not have been initialized yet.
Expand Down Expand Up @@ -612,6 +612,7 @@ void SetupServerArgs()
gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-tdsinglekeycheck", "Set the single key check flag for transferdomain RPC. If enabled, transfers between domain are only allowed if the addresses specified corresponds to the same key (default: true)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
gArgs.AddArg("-evmtxpriorityfeepercentile", strprintf("Set the suggested priority fee for EVM transactions (default: %u)", DEFAULT_SUGGESTED_PRIORITY_FEE_PERCENTILE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);

SetupChainParamsBaseOptions();
Expand Down
35 changes: 19 additions & 16 deletions test/functional/feature_evm_fee.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,19 +198,19 @@ def test_max_gas_price(self):
},
)

# # Test insufficient balance due to high gas fees
# assert_raises_rpc_error(
# -32001,
# "evm tx failed to validate prepay fee value overflow",
# self.nodes[0].eth_sendTransaction,
# {
# "from": self.ethAddress,
# "to": self.toAddress,
# "value": "0x7148", # 29_000
# "gas": "0x7a120",
# "gasPrice": "0xfffffffffffffff",
# },
# )
# Test insufficient balance due to high gas fees
assert_raises_rpc_error(
-32001,
"evm tx failed to pre-validate insufficient balance to pay fees",
self.nodes[0].eth_sendTransaction,
{
"from": self.ethAddress,
"to": self.toAddress,
"value": "0x7148", # 29_000
"gas": "0x7a120",
"gasPrice": "0xfffffffffffffff",
},
)

self.rollback_to(height)

Expand Down Expand Up @@ -262,7 +262,10 @@ def test_fee_deduction_empty_balance(self):
emptyAddress = self.nodes[0].getnewaddress("", "erc55")
balance = self.nodes[0].eth_getBalance(emptyAddress, "latest")
assert_equal(int(balance[2:], 16), 000000000000000000000)
self.nodes[0].eth_sendTransaction(
assert_raises_rpc_error(
-32001,
"evm tx failed to pre-validate insufficient balance to pay fees",
self.nodes[0].eth_sendTransaction,
{
"from": emptyAddress,
"to": self.toAddress,
Expand All @@ -273,7 +276,7 @@ def test_fee_deduction_empty_balance(self):
)
self.nodes[0].generate(1)
block = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
# Tx should be valid and enter the mempool, but will not be minted into the block
# Check for empty block
assert_equal(len(block["tx"]), 1)

self.rollback_to(height)
Expand Down Expand Up @@ -343,7 +346,7 @@ def run_test(self):

self.test_gas_limit_higher_than_block_limit()

# self.test_fee_deduction_empty_balance() // TODO assert correct behaviour
self.test_fee_deduction_empty_balance()

self.test_fee_deduction_send_full_balance()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,54 @@ def mine_block_with_legacy_txs(self):
self.nodes[0].eth_sendTransaction(tx)
self.nodes[0].generate(1)

def test_suggest_priority_fee(self):
self.rollback_to(self.startHeight)

numBlocks = 10
self.mine_block_with_eip1559_txs(numBlocks)

# Default suggested priority fee calculation is at 60%
correctPriorityFeeIdx = int((len(self.priorityFees) - 1) * 0.6)
suggestedFee = self.nodes[0].eth_maxPriorityFeePerGas()
assert_equal(suggestedFee, hex(self.priorityFees[correctPriorityFeeIdx]))

def test_incremental_suggest_priority_fee(self):
self.rollback_to(self.startHeight)

numBlocks = 20
priorityFee = 0
for _ in range(numBlocks):
nonce = self.nodes[0].w3.eth.get_transaction_count(self.ethAddress)
for _ in range(10):
tx = {
"from": self.ethAddress,
"value": "0x0",
"data": CONTRACT_BYTECODE,
"gas": "0x18e70", # 102_000
"maxPriorityFeePerGas": hex(priorityFee),
"maxFeePerGas": "0x22ecb25c00", # 150_000_000_000
"type": "0x2",
"nonce": hex(nonce),
}
nonce += 1
priorityFee += 1
self.nodes[0].eth_sendTransaction(tx)
self.nodes[0].generate(1)

# Default suggested priority fee calculation is at 60%
priorityFee -= 1
correctPriorityFee = int(priorityFee * 0.6)
suggestedFee = self.nodes[0].eth_maxPriorityFeePerGas()
assert_equal(suggestedFee, hex(correctPriorityFee))

def test_suggest_priority_fee_empty_blocks(self):
self.rollback_to(self.startHeight)

# generate empty blocks
self.nodes[0].generate(20)
suggested_fee = self.nodes[0].eth_maxPriorityFeePerGas()
assert_equal("0x0", suggested_fee)

def test_fee_history_eip1559_txs(self):
self.rollback_to(self.startHeight)

Expand Down Expand Up @@ -211,10 +259,7 @@ def test_fee_history_empty_percentile(self):
for gasUsedRatio in history["gasUsedRatio"]:
assert_equal(Decimal(str(gasUsedRatio)), Decimal("0.033868333333333334"))

assert_equal(len(history["reward"]), numBlocks)
for reward in history["reward"]:
assert_equal(len(reward), len(rewardPercentiles))
assert_equal(reward, [])
assert_equal(history["reward"], None)

def test_invalid_fee_history_rpc(self):
self.rollback_to(self.startHeight)
Expand All @@ -223,6 +268,7 @@ def test_invalid_fee_history_rpc(self):
self.mine_block_with_eip1559_txs(numBlocks)
rewardPercentiles = []
aboveLimitPercentiles = [101, 20, 30, 40, 100]
belowLimitPercentiles = [-1, 20, 30, 40, 100]
notIncreasingPercentiles = [10, 20, 30, 50, 40, 100]
tooManyPercentiles = [0]
for i in range(100):
Expand Down Expand Up @@ -258,6 +304,16 @@ def test_invalid_fee_history_rpc(self):
tooManyPercentiles,
)

# Test invalid feeHistory call, percentile value less than inclusive range
assert_raises_rpc_error(
-32001,
"Percentile value less than inclusive range of 0",
self.nodes[0].eth_feeHistory,
hex(numBlocks),
"latest",
belowLimitPercentiles,
)

# Test invalid feeHistory call, percentile value exceed inclusive range
assert_raises_rpc_error(
-32001,
Expand All @@ -283,6 +339,13 @@ def run_test(self):

self.nodes[0].generate(1)

self.test_suggest_priority_fee()

self.test_incremental_suggest_priority_fee()

# Also checks rollback pipeline to ensure cache is cleared
self.test_suggest_priority_fee_empty_blocks()

self.test_fee_history_eip1559_txs()

self.test_fee_history_legacy_txs()
Expand Down
2 changes: 1 addition & 1 deletion test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@
"feature_evm_rollback.py",
"feature_evm_rpc_transaction.py",
"feature_evm_rpc.py",
"feature_evm_rpc_fee_history.py",
"feature_evm_rpc_fee.py",
"feature_evm_rpc_filters.py",
"feature_evm_smart_contract.py",
"feature_evm_transaction_replacement.py",
Expand Down
Loading