Skip to content

Commit

Permalink
EVM: DST20 migration TX (#2327)
Browse files Browse the repository at this point in the history
* Add DST20 migration TX and receipt

* Temporary disable of ValidateCoinbaseXVMOutput

* Temporary disable of rollback related test failure

* Extract contract (tx, receipt) creation

* Fix test balance issue

* Revert "Temporary disable of ValidateCoinbaseXVMOutput"

This reverts commit 91fa9ae.

* Fix test

* Refactor to add migration txs in construct_block when genesis EVM block

* Get loan tokens from cache

* Restore failing test

* Remove block cloning

* Fix hash used in recover sender address

* Use same sender address for receipt and SignedTx

* Index bytecode on migration

* Restore failing test

* Use token_id as migratory TX nonce
  • Loading branch information
Jouzo authored Aug 17, 2023
1 parent 7fda957 commit f278de4
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 146 deletions.
4 changes: 2 additions & 2 deletions lib/ain-contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ pub fn get_dst20_codehash() -> Result<H256> {
Ok(Blake2Hasher::hash(&bytecode))
}

pub fn dst20_address_from_token_id(token_id: &str) -> Result<H160> {
let number_str = format!("{:x}", token_id.parse::<u64>()?);
pub fn dst20_address_from_token_id(token_id: u64) -> Result<H160> {
let number_str = format!("{:x}", token_id);
let padded_number_str = format!("{number_str:0>38}");
let final_str = format!("ff{padded_number_str}");

Expand Down
9 changes: 9 additions & 0 deletions lib/ain-cpp-imports/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ pub mod ffi {
pub finality_count: u64,
}

#[derive(Debug, Clone)]
pub struct DST20Token {
pub id: u64,
pub name: String,
pub symbol: String,
}

unsafe extern "C++" {
include!("ffi/ffiexports.h");
type Attributes;
type DST20Token;

fn getChainId() -> u64;
fn isMining() -> bool;
Expand All @@ -28,5 +36,6 @@ pub mod ffi {
fn getCurrentHeight() -> i32;
fn getAttributeDefaults() -> Attributes;
fn CppLogPrintf(message: String);
fn getDST20Tokens(mnview_ptr: usize) -> Vec<DST20Token>;
}
}
14 changes: 14 additions & 0 deletions lib/ain-cpp-imports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ mod ffi {
pub finality_count: u64,
}

pub struct DST20Token {
pub id: u64,
pub name: String,
pub symbol: String,
}

const UNIMPL_MSG: &str = "This cannot be used on a test path";
pub fn getChainId() -> u64 {
unimplemented!("{}", UNIMPL_MSG)
Expand Down Expand Up @@ -69,6 +75,10 @@ mod ffi {
// Intentionally left empty, so it can be used from everywhere.
// Just the logs are skipped.
}

pub fn getDST20Tokens(_mnview_ptr: usize) -> Vec<DST20Token> {
unimplemented!("{}", UNIMPL_MSG)
}
}

pub use ffi::Attributes;
Expand Down Expand Up @@ -155,5 +165,9 @@ pub fn log_print(message: &str) {
ffi::CppLogPrintf(message.to_owned());
}

pub fn get_dst20_tokens(mnview_ptr: usize) -> Vec<ffi::DST20Token> {
ffi::getDST20Tokens(mnview_ptr)
}

#[cfg(test)]
mod tests {}
108 changes: 89 additions & 19 deletions lib/ain-evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::sync::Arc;

use ain_contracts::{Contracts, CONTRACT_ADDRESSES};
use anyhow::format_err;
use ethereum::{Block, PartialHeader, ReceiptV3};
use ethereum::{
Block, EIP1559ReceiptData, LegacyTransaction, PartialHeader, ReceiptV3, TransactionAction,
TransactionSignature, TransactionV2,
};
use ethereum_types::{Bloom, H160, H64, U256};
use log::debug;
use primitive_types::H256;
Expand All @@ -22,9 +25,9 @@ use crate::storage::traits::BlockStorage;
use crate::storage::Storage;
use crate::traits::Executor;
use crate::transaction::system::{BalanceUpdate, DST20Data, DeployContractData, SystemTx};
use crate::transaction::SignedTx;
use crate::transaction::{SignedTx, LOWER_H256};
use crate::trie::GENESIS_STATE_ROOT;
use crate::txqueue::{BlockData, QueueTx};
use crate::txqueue::{BlockData, QueueTx, QueueTxItem};
use crate::Result;

pub struct EVMServices {
Expand Down Expand Up @@ -55,6 +58,8 @@ pub struct DST20BridgeInfo {
pub storage: Vec<(H256, H256)>,
}

pub type ReceiptAndOptionalContractAddress = (ReceiptV3, Option<H160>);

impl EVMServices {
/// Constructs a new Handlers instance. Depending on whether the defid -ethstartstate flag is set,
/// it either revives the storage from a previously saved state or initializes new storage using input from a JSON file.
Expand Down Expand Up @@ -114,13 +119,22 @@ impl EVMServices {
beneficiary: H160,
timestamp: u64,
dvm_block_number: u64,
mnview_ptr: usize,
) -> Result<FinalizedBlockInfo> {
let tx_queue = self.core.tx_queues.get(queue_id)?;
let mut queue = tx_queue.data.lock().unwrap();

let is_evm_genesis_block = queue.target_block == U256::zero();
if is_evm_genesis_block {
let migration_txs = get_dst20_migration_txs(mnview_ptr)?;
queue.transactions.extend(migration_txs.into_iter())
}

let queue_txs_len = queue.transactions.len();
let mut all_transactions = Vec::with_capacity(queue_txs_len);
let mut failed_transactions = Vec::with_capacity(queue_txs_len);
let mut receipts_v3: Vec<ReceiptV3> = Vec::with_capacity(queue_txs_len);
let mut receipts_v3: Vec<ReceiptAndOptionalContractAddress> =
Vec::with_capacity(queue_txs_len);
let mut total_gas_used = 0u64;
let mut total_gas_fees = U256::zero();
let mut logs_bloom: Bloom = Bloom::default();
Expand Down Expand Up @@ -188,8 +202,7 @@ impl EVMServices {
} = EVMServices::counter_contract(dvm_block_number, current_block_number)?;
executor.update_storage(address, storage)?;
}

for queue_item in queue.transactions.clone() {
for (idx, queue_item) in queue.transactions.clone().into_iter().enumerate() {
match queue_item.tx {
QueueTx::SignedTx(signed_tx) => {
let nonce = executor.get_nonce(&signed_tx.sender);
Expand All @@ -208,11 +221,11 @@ impl EVMServices {
receipt,
) = executor.exec(&signed_tx, prepay_gas);
debug!(
"receipt : {:#?} for signed_tx : {:#x}",
"receipt : {:#?}, exit_reason {:#?} for signed_tx : {:#x}",
receipt,
exit_reason,
signed_tx.transaction.hash()
);

if !exit_reason.is_succeed() {
failed_transactions.push(hex::encode(queue_item.tx_hash));
}
Expand All @@ -223,7 +236,7 @@ impl EVMServices {

all_transactions.push(signed_tx.clone());
EVMCoreService::logs_bloom(logs, &mut logs_bloom);
receipts_v3.push(receipt);
receipts_v3.push((receipt, None));
}
QueueTx::SystemTx(SystemTx::EvmIn(BalanceUpdate { address, amount })) => {
debug!(
Expand All @@ -250,6 +263,7 @@ impl EVMServices {
name,
symbol,
address,
token_id,
})) => {
debug!(
"[construct_block] DeployContract for address {}, name {}, symbol {}",
Expand All @@ -262,9 +276,18 @@ impl EVMServices {
storage,
} = EVMServices::dst20_contract(&mut executor, address, name, symbol)?;

if let Err(e) = executor.deploy_contract(address, bytecode, storage) {
if let Err(e) = executor.deploy_contract(address, bytecode.clone(), storage) {
debug!("[construct_block] EvmOut failed with {e}");
}
let (tx, receipt) = create_deploy_contract_tx(
token_id,
current_block_number,
&base_fee,
bytecode.into_vec(),
)?;

all_transactions.push(Box::new(tx));
receipts_v3.push((receipt, Some(address)));
}
QueueTx::SystemTx(SystemTx::DST20Bridge(DST20Data {
to,
Expand Down Expand Up @@ -336,19 +359,17 @@ impl EVMServices {
Vec::new(),
);

let block_hash = *block.header.hash().as_fixed_bytes();
let receipts = self.receipt.generate_receipts(
&all_transactions,
receipts_v3,
block.header.hash(),
block.header.number,
);
queue.block_data = Some(BlockData {
block: block.clone(),
receipts,
});
queue.block_data = Some(BlockData { block, receipts });

Ok(FinalizedBlockInfo {
block_hash: *block.header.hash().as_fixed_bytes(),
block_hash,
failed_transactions,
total_burnt_fees,
total_priority_fees,
Expand Down Expand Up @@ -570,7 +591,7 @@ impl EVMServices {
queue_id: u64,
name: &str,
symbol: &str,
token_id: &str,
token_id: u64,
) -> Result<bool> {
let address = ain_contracts::dst20_address_from_token_id(token_id)?;
debug!(
Expand All @@ -592,6 +613,7 @@ impl EVMServices {
let deploy_tx = QueueTx::SystemTx(SystemTx::DeployContract(DeployContractData {
name: String::from(name),
symbol: String::from(symbol),
token_id,
address,
}));

Expand All @@ -603,9 +625,7 @@ impl EVMServices {
pub fn reserve_dst20_namespace(&self, executor: &mut AinExecutor) -> Result<()> {
let bytecode = ain_contracts::get_system_reserved_bytecode()?;
let addresses = (1..=1024)
.map(|token_id| {
ain_contracts::dst20_address_from_token_id(&token_id.to_string()).unwrap()
})
.map(|token_id| ain_contracts::dst20_address_from_token_id(token_id).unwrap())
.collect::<Vec<H160>>();

for address in addresses {
Expand All @@ -616,3 +636,53 @@ impl EVMServices {
Ok(())
}
}

fn create_deploy_contract_tx(
token_id: u64,
block_number: U256,
base_fee: &U256,
bytecode: Vec<u8>,
) -> Result<(SignedTx, ReceiptV3)> {
let tx = TransactionV2::Legacy(LegacyTransaction {
nonce: U256::from(token_id),
gas_price: base_fee.clone(),
gas_limit: U256::from(u64::MAX),
action: TransactionAction::Create,
value: U256::zero(),
input: bytecode,
signature: TransactionSignature::new(27, LOWER_H256, LOWER_H256)
.ok_or(format_err!("Invalid transaction signature format"))?,
})
.try_into()?;

let receipt = ReceiptV3::Legacy(EIP1559ReceiptData {
status_code: 1u8,
used_gas: U256::zero(),
logs_bloom: Bloom::default(),
logs: Vec::new(),
});

Ok((tx, receipt))
}

fn get_dst20_migration_txs(mnview_ptr: usize) -> Result<Vec<QueueTxItem>> {
let mut txs = Vec::new();
for token in ain_cpp_imports::get_dst20_tokens(mnview_ptr) {
let address = ain_contracts::dst20_address_from_token_id(token.id)?;
debug!("Deploying to address {:#?}", address);

let tx = QueueTx::SystemTx(SystemTx::DeployContract(DeployContractData {
name: token.name,
symbol: token.symbol,
token_id: token.id,
address,
}));
txs.push(QueueTxItem {
tx,
tx_hash: Default::default(),
tx_fee: U256::zero(),
gas_used: U256::zero(),
});
}
Ok(txs)
}
20 changes: 11 additions & 9 deletions lib/ain-evm/src/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use primitive_types::{H160, H256, U256};
use rlp::RlpStream;
use serde::{Deserialize, Serialize};

use crate::evm::ReceiptAndOptionalContractAddress;
use crate::storage::{traits::ReceiptStorage, Storage};
use crate::transaction::SignedTx;
use crate::Result;
Expand Down Expand Up @@ -43,18 +44,18 @@ impl ReceiptService {
Self { storage }
}

pub fn get_receipts_root(receipts: &[ReceiptV3]) -> H256 {
pub fn get_receipts_root(receipts: &[ReceiptAndOptionalContractAddress]) -> H256 {
ordered_trie_root(
receipts
.iter()
.map(|r| EnvelopedEncodable::encode(r).freeze()),
.map(|(r, _)| EnvelopedEncodable::encode(r).freeze()),
)
}

pub fn generate_receipts(
&self,
transactions: &[Box<SignedTx>],
receipts: Vec<ReceiptV3>,
receipts_and_contract_address: Vec<ReceiptAndOptionalContractAddress>,
block_hash: H256,
block_number: U256,
) -> Vec<Receipt> {
Expand All @@ -64,8 +65,8 @@ impl ReceiptService {
transactions
.iter()
.enumerate()
.zip(receipts)
.map(|((index, signed_tx), receipt)| {
.zip(receipts_and_contract_address)
.map(|((index, signed_tx), (receipt, contract_address))| {
let receipt_data = match &receipt {
ReceiptV3::Legacy(data)
| ReceiptV3::EIP2930(data)
Expand All @@ -84,10 +85,11 @@ impl ReceiptService {
to: signed_tx.to(),
tx_index: index,
tx_type: signed_tx.transaction.type_id().unwrap_or_default(),
contract_address: signed_tx
.to()
.is_none()
.then(|| get_contract_address(&signed_tx.sender, &signed_tx.nonce())),
contract_address: signed_tx.to().is_none().then(|| {
contract_address.unwrap_or_else(|| {
get_contract_address(&signed_tx.sender, &signed_tx.nonce())
})
}),
logs_index: logs_size - logs_len,
cumulative_gas,
}
Expand Down
Loading

0 comments on commit f278de4

Please sign in to comment.