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

Fixes for EVM block size limit #2180

Merged
merged 14 commits into from
Jul 18, 2023
24 changes: 18 additions & 6 deletions lib/ain-evm/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ impl EVMCoreService {
pub fn validate_raw_tx(
&self,
tx: &str,
with_gas_usage: bool,
) -> Result<(SignedTx, u64), Box<dyn Error>> {
call_tx: bool,
context: u64,
) -> Result<(SignedTx, U256, u64), Box<dyn Error>> {
sieniven marked this conversation as resolved.
Show resolved Hide resolved
debug!("[validate_raw_tx] raw transaction : {:#?}", tx);
let buffer = <Vec<u8>>::from_hex(tx)?;
let tx: TransactionV2 = ethereum::EnvelopedDecodable::decode(&buffer)
Expand Down Expand Up @@ -225,7 +226,7 @@ impl EVMCoreService {
return Err(anyhow!("Gas limit higher than MAX_GAS_PER_BLOCK").into());
}

let used_gas = if with_gas_usage {
let used_gas = if call_tx {
let TxResponse { used_gas, .. } = self.call(EthCallArgs {
caller: Some(signed_tx.sender),
to: signed_tx.to(),
Expand All @@ -240,7 +241,18 @@ impl EVMCoreService {
u64::default()
};

Ok((signed_tx, used_gas))
if call_tx {
let total_current_gas_used = self
.tx_queues
.get_total_gas_used(context)
.unwrap_or_default();

if U256::from(total_current_gas_used + used_gas) > MAX_GAS_PER_BLOCK {
return Err(anyhow!("Block size limit is more than MAX_GAS_PER_BLOCK").into());
}
}

Ok((signed_tx, prepay_gas, used_gas))
}

pub fn logs_bloom(logs: Vec<Log>, bloom: &mut Bloom) {
Expand All @@ -263,7 +275,7 @@ impl EVMCoreService {
hash: NativeTxHash,
) -> Result<(), EVMError> {
let queue_tx = QueueTx::BridgeTx(BridgeTx::EvmIn(BalanceUpdate { address, amount }));
self.tx_queues.queue_tx(context, queue_tx, hash)?;
self.tx_queues.queue_tx(context, queue_tx, hash, 0u64)?;
Ok(())
}

Expand All @@ -288,7 +300,7 @@ impl EVMCoreService {
.into())
} else {
let queue_tx = QueueTx::BridgeTx(BridgeTx::EvmOut(BalanceUpdate { address, amount }));
self.tx_queues.queue_tx(context, queue_tx, hash)?;
self.tx_queues.queue_tx(context, queue_tx, hash, 0u64)?;
Ok(())
}
}
Expand Down
12 changes: 10 additions & 2 deletions lib/ain-evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,16 @@ impl EVMServices {
Ok(())
}

pub fn queue_tx(&self, context: u64, tx: QueueTx, hash: NativeTxHash) -> Result<(), EVMError> {
self.core.tx_queues.queue_tx(context, tx.clone(), hash)?;
pub fn queue_tx(
&self,
context: u64,
tx: QueueTx,
hash: NativeTxHash,
gas_used: u64,
) -> Result<(), EVMError> {
self.core
.tx_queues
.queue_tx(context, tx.clone(), hash, gas_used)?;

if let QueueTx::SignedTx(signed_tx) = tx {
self.filters.add_tx_to_filters(signed_tx.transaction.hash())
Expand Down
48 changes: 35 additions & 13 deletions lib/ain-evm/src/txqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,14 @@ impl TransactionQueueMap {
context_id: u64,
tx: QueueTx,
hash: NativeTxHash,
gas_used: u64,
) -> Result<(), QueueError> {
self.queues
.read()
.unwrap()
.get(&context_id)
.ok_or(QueueError::NoSuchContext)
.map(|queue| queue.queue_tx((tx, hash)))?
.map(|queue| queue.queue_tx((tx, hash), gas_used))?
}

/// `drain_all` returns all transactions from the `TransactionQueue` associated with the
Expand Down Expand Up @@ -142,6 +143,14 @@ impl TransactionQueueMap {
.get(&context_id)
.and_then(|queue| queue.get_next_valid_nonce(address))
}

pub fn get_total_gas_used(&self, context_id: u64) -> Option<u64> {
self.queues
.read()
.unwrap()
.get(&context_id)
.map(|queue| queue.get_total_gas_used())
}
}

#[derive(Debug, Clone)]
Expand All @@ -159,21 +168,28 @@ type QueueTxWithNativeHash = (QueueTx, NativeTxHash);
pub struct TransactionQueue {
transactions: Mutex<Vec<QueueTxWithNativeHash>>,
account_nonces: Mutex<HashMap<H160, U256>>,
total_gas_used: Mutex<u64>,
}

impl TransactionQueue {
pub fn new() -> Self {
Self {
transactions: Mutex::new(Vec::new()),
account_nonces: Mutex::new(HashMap::new()),
total_gas_used: Mutex::new(0u64),
}
}

pub fn clear(&self) {
self.transactions.lock().unwrap().clear();
let mut total_gas_used = self.total_gas_used.lock().unwrap();
*total_gas_used = 0u64;
}

pub fn drain_all(&self) -> Vec<QueueTxWithNativeHash> {
let mut total_gas_used = self.total_gas_used.lock().unwrap();
*total_gas_used = 0u64;

self.transactions
.lock()
.unwrap()
Expand All @@ -185,7 +201,7 @@ impl TransactionQueue {
self.transactions.lock().unwrap().clone()
}

pub fn queue_tx(&self, tx: QueueTxWithNativeHash) -> Result<(), QueueError> {
pub fn queue_tx(&self, tx: QueueTxWithNativeHash, gas_used: u64) -> Result<(), QueueError> {
if let QueueTx::SignedTx(signed_tx) = &tx.0 {
let mut account_nonces = self.account_nonces.lock().unwrap();
if let Some(nonce) = account_nonces.get(&signed_tx.sender) {
Expand All @@ -196,6 +212,8 @@ impl TransactionQueue {
account_nonces.insert(signed_tx.sender, signed_tx.nonce());
}
self.transactions.lock().unwrap().push(tx);
let mut total_gas_used = self.total_gas_used.lock().unwrap();
*total_gas_used += gas_used;
Ok(())
}

Expand All @@ -222,6 +240,10 @@ impl TransactionQueue {
.map(ToOwned::to_owned)
.map(|nonce| nonce + 1)
}

pub fn get_total_gas_used(&self) -> u64 {
*self.total_gas_used.lock().unwrap()
}
}

impl From<SignedTx> for QueueTx {
Expand Down Expand Up @@ -270,10 +292,10 @@ mod tests {
// Nonce 0, sender 0xe61a3a6eb316d773c773f4ce757a542f673023c6
let tx3 = QueueTx::SignedTx(Box::new(SignedTx::try_from("f869808502540be400832dc6c0943e338e722607a8c1eab615579ace4f6dedfa19fa80840adb1a9a2aa03d28d24808c3de08c606c5544772ded91913f648ad56556f181905208e206c85a00ecd0ba938fb89fc4a17ea333ea842c7305090dee9236e2b632578f9e5045cb3").unwrap()));

queue.queue_tx((tx1, H256::from_low_u64_be(1).into()))?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()))?;
queue.queue_tx((tx1, H256::from_low_u64_be(1).into()), 0u64)?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()), 0u64)?;
// Should fail as nonce 2 is already queued for this sender
let queued = queue.queue_tx((tx3, H256::from_low_u64_be(3).into()));
let queued = queue.queue_tx((tx3, H256::from_low_u64_be(3).into()), 0u64);
assert!(matches!(queued, Err(QueueError::InvalidNonce { .. })));
Ok(())
}
Expand All @@ -297,11 +319,11 @@ mod tests {
// Nonce 0, sender 0xe61a3a6eb316d773c773f4ce757a542f673023c6
let tx4 = QueueTx::SignedTx(Box::new(SignedTx::try_from("f869808502540be400832dc6c0943e338e722607a8c1eab615579ace4f6dedfa19fa80840adb1a9a2aa03d28d24808c3de08c606c5544772ded91913f648ad56556f181905208e206c85a00ecd0ba938fb89fc4a17ea333ea842c7305090dee9236e2b632578f9e5045cb3").unwrap()));

queue.queue_tx((tx1, H256::from_low_u64_be(1).into()))?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()))?;
queue.queue_tx((tx3, H256::from_low_u64_be(3).into()))?;
queue.queue_tx((tx1, H256::from_low_u64_be(1).into()), 0u64)?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()), 0u64)?;
queue.queue_tx((tx3, H256::from_low_u64_be(3).into()), 0u64)?;
// Should fail as nonce 2 is already queued for this sender
let queued = queue.queue_tx((tx4, H256::from_low_u64_be(4).into()));
let queued = queue.queue_tx((tx4, H256::from_low_u64_be(4).into()), 0u64);
assert!(matches!(queued, Err(QueueError::InvalidNonce { .. })));
Ok(())
}
Expand All @@ -322,10 +344,10 @@ mod tests {
// Nonce 2, sender 0x6bc42fd533d6cb9d973604155e1f7197a3b0e703
let tx4 = QueueTx::SignedTx(Box::new(SignedTx::try_from("f869028502540be400832dc6c0943e338e722607a8c1eab615579ace4f6dedfa19fa80840adb1a9a2aa09588b47d2cd3f474d6384309cca5cb8e360cb137679f0a1589a1c184a15cb27ca0453ddbf808b83b279cac3226b61a9d83855aba60ae0d3a8407cba0634da7459d").unwrap()));

queue.queue_tx((tx1, H256::from_low_u64_be(1).into()))?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()))?;
queue.queue_tx((tx3, H256::from_low_u64_be(3).into()))?;
queue.queue_tx((tx4, H256::from_low_u64_be(4).into()))?;
queue.queue_tx((tx1, H256::from_low_u64_be(1).into()), 0u64)?;
queue.queue_tx((tx2, H256::from_low_u64_be(2).into()), 0u64)?;
queue.queue_tx((tx3, H256::from_low_u64_be(3).into()), 0u64)?;
queue.queue_tx((tx4, H256::from_low_u64_be(4).into()), 0u64)?;
Ok(())
}
}
32 changes: 20 additions & 12 deletions lib/ain-rs-exports/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ pub fn evm_sub_balance(context: u64, address: &str, amount: [u8; 32], hash: [u8;
pub fn evm_try_prevalidate_raw_tx(
result: &mut ffi::CrossBoundaryResult,
tx: &str,
with_gas_usage: bool,
call_tx: bool,
context: u64,
) -> ffi::ValidateTxCompletion {
match SERVICES.evm.verify_tx_fees(tx) {
Ok(_) => (),
Expand All @@ -218,14 +219,15 @@ pub fn evm_try_prevalidate_raw_tx(
}
}

match SERVICES.evm.core.validate_raw_tx(tx, with_gas_usage) {
Ok((signed_tx, used_gas)) => {
match SERVICES.evm.core.validate_raw_tx(tx, call_tx, context) {
Ok((signed_tx, tx_fees, gas_used)) => {
result.ok = true;

ffi::ValidateTxCompletion {
nonce: signed_tx.nonce().as_u64(),
sender: signed_tx.sender.to_fixed_bytes(),
used_gas,
tx_fees: tx_fees.try_into().unwrap_or_default(),
gas_used,
}
}
Err(e) => {
Expand Down Expand Up @@ -276,18 +278,24 @@ pub fn evm_try_queue_tx(
context: u64,
raw_tx: &str,
hash: [u8; 32],
gas_used: u64,
) {
let signed_tx: Result<SignedTx, TransactionError> = raw_tx.try_into();
match signed_tx {
Ok(signed_tx) => match SERVICES.evm.queue_tx(context, signed_tx.into(), hash) {
Ok(_) => {
result.ok = true;
}
Err(e) => {
result.ok = false;
result.reason = e.to_string();
Ok(signed_tx) => {
match SERVICES
.evm
.queue_tx(context, signed_tx.into(), hash, gas_used)
{
Ok(_) => {
result.ok = true;
}
Err(e) => {
result.ok = false;
result.reason = e.to_string();
}
}
},
}
Err(e) => {
result.ok = false;
result.reason = e.to_string();
Expand Down
9 changes: 6 additions & 3 deletions lib/ain-rs-exports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ pub mod ffi {
pub struct ValidateTxCompletion {
pub nonce: u64,
pub sender: [u8; 20],
pub used_gas: u64,
pub tx_fees: u64,
pub gas_used: u64,
}

extern "Rust" {
Expand Down Expand Up @@ -79,13 +80,15 @@ pub mod ffi {
fn evm_try_prevalidate_raw_tx(
result: &mut CrossBoundaryResult,
tx: &str,
with_gas_usage: bool,
call_tx: bool,
context: u64,
) -> ValidateTxCompletion;
fn evm_try_queue_tx(
result: &mut CrossBoundaryResult,
context: u64,
raw_tx: &str,
native_tx_hash: [u8; 32],
hash: [u8; 32],
gas_used: u64,
);
fn evm_try_finalize(
result: &mut CrossBoundaryResult,
Expand Down
3 changes: 1 addition & 2 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c

if (NotAllowedToFail(txType, nSpendHeight) || (nSpendHeight >= chainparams.GetConsensus().GrandCentralHeight && txType == CustomTxType::UpdateMasternode)) {
CCustomCSView discardCache(mnview, nullptr, nullptr, nullptr);
uint64_t gasUsed{};
auto res = ApplyCustomTx(discardCache, inputs, tx, chainparams.GetConsensus(), nSpendHeight, gasUsed, 0, &canSpend);
auto res = ApplyCustomTx(discardCache, inputs, tx, chainparams.GetConsensus(), nSpendHeight, 0, &canSpend);
if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) {
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-customtx", res.msg);
}
Expand Down
19 changes: 6 additions & 13 deletions src/masternodes/mn_checks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,6 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
uint64_t time;
uint32_t txn;
uint64_t evmContext;
uint64_t &gasUsed;

public:
CCustomTxApplyVisitor(const CTransaction &tx,
Expand All @@ -779,14 +778,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
const Consensus::Params &consensus,
uint64_t time,
uint32_t txn,
const uint64_t evmContext,
uint64_t &gasUsed)
const uint64_t evmContext)

: CCustomTxVisitor(tx, height, coins, mnview, consensus),
time(time),
txn(txn),
evmContext(evmContext),
gasUsed(gasUsed) {}
evmContext(evmContext) {}

Res operator()(const CCreateMasterNodeMessage &obj) const {
Require(CheckMasternodeCreationTx());
Expand Down Expand Up @@ -3930,7 +3927,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
return Res::Err("evm tx size too large");

CrossBoundaryResult result;
const auto hashAndGas = evm_try_prevalidate_raw_tx(result, HexStr(obj.evmTx), true);
const auto prevalidateResults = evm_try_prevalidate_raw_tx(result, HexStr(obj.evmTx), true, evmContext);

// Completely remove this fork guard on mainnet upgrade to restore nonce check from EVM activation
if (height >= static_cast<uint32_t>(consensus.ChangiIntermediateHeight)) {
Expand All @@ -3940,14 +3937,12 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor {
}
}

evm_try_queue_tx(result, evmContext, HexStr(obj.evmTx), tx.GetHash().GetByteArray());
evm_try_queue_tx(result, evmContext, HexStr(obj.evmTx), tx.GetHash().GetByteArray(), prevalidateResults.gas_used);
if (!result.ok) {
LogPrintf("[evm_try_queue_tx] failed, reason : %s\n", result.reason);
return Res::Err("evm tx failed to queue %s\n", result.reason);
}

gasUsed = hashAndGas.used_gas;

std::vector<unsigned char> evmTxHashBytes;
sha3(obj.evmTx, evmTxHashBytes);
auto txHash = tx.GetHash();
Expand Down Expand Up @@ -4174,7 +4169,6 @@ Res CustomTxVisit(CCustomCSView &mnview,
const Consensus::Params &consensus,
const CCustomTxMessage &txMessage,
uint64_t time,
uint64_t &gasUsed,
uint32_t txn,
const uint64_t evmContext) {
if (IsDisabledTx(height, tx, consensus)) {
Expand All @@ -4183,7 +4177,7 @@ Res CustomTxVisit(CCustomCSView &mnview,
auto context = evmContext;
if (context == 0) context = evm_get_context();
try {
return std::visit(CCustomTxApplyVisitor(tx, height, coins, mnview, consensus, time, txn, context, gasUsed), txMessage);
return std::visit(CCustomTxApplyVisitor(tx, height, coins, mnview, consensus, time, txn, context), txMessage);
} catch (const std::bad_variant_access &e) {
return Res::Err(e.what());
} catch (...) {
Expand Down Expand Up @@ -4266,7 +4260,6 @@ Res ApplyCustomTx(CCustomCSView &mnview,
const CTransaction &tx,
const Consensus::Params &consensus,
uint32_t height,
uint64_t &gasUsed,
uint64_t time,
uint256 *canSpend,
uint32_t txn,
Expand Down Expand Up @@ -4294,7 +4287,7 @@ Res ApplyCustomTx(CCustomCSView &mnview,
PopulateVaultHistoryData(mnview.GetHistoryWriters(), view, txMessage, txType, height, txn, tx.GetHash());
}

res = CustomTxVisit(view, coins, tx, height, consensus, txMessage, time, gasUsed, txn, evmContext);
res = CustomTxVisit(view, coins, tx, height, consensus, txMessage, time, txn, evmContext);

if (res) {
if (canSpend && txType == CustomTxType::UpdateMasternode) {
Expand Down
Loading