Skip to content

Commit

Permalink
Feat(evm): Add feature that change structure of evm unsigned transact…
Browse files Browse the repository at this point in the history
…ion.

And fix big transaction deallocation.
  • Loading branch information
vldm committed Aug 18, 2021
1 parent cc48265 commit 5f5fa9a
Show file tree
Hide file tree
Showing 11 changed files with 339 additions and 70 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ $ cargo test --no-fail-fast
Info about EVM integration is at our [docs](https://docs.velas.com/evm).

### Starting a local testnet
Start your own devnet locally, instructions are in the [online docs](https://docs.velas.com/cluster/bench-tps).
Start your own Development network locally, instructions are in the [online docs](https://docs.velas.com/cluster/bench-tps).

### Accessing the remote testnet and mainnet
* `testnet` - public devnet accessible via bootstrap.testnet.veladev.net.
* `mainnet` - public devnet accessible via bootstrap.velas.com.
* `testnet` - public accessible via bootstrap.testnet.veladev.net.
* `mainnet` - public accessible via bootstrap.velas.com.

# Benchmarking

Expand Down
5 changes: 3 additions & 2 deletions evm-utils/evm-rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ impl RPCTransaction {
),
),
};
let caller_addr = tx.caller.to_string();
let v = chain_id * 2 + 35;
(
to,
Expand All @@ -687,8 +688,8 @@ impl RPCTransaction {
value,
nonce,
v,
U256::zero(),
U256::zero(),
Hex::<U256>::from_hex(&caller_addr)?.0,
U256::from(0x1),
)
}
};
Expand Down
81 changes: 66 additions & 15 deletions evm-utils/evm-state/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,21 @@ impl Executor {
&mut self,
caller: H160,
tx: UnsignedTransaction,
calculate_tx_hash_with_caller: bool,
precompiles: F,
) -> Result<ExecutionResult, Error>
where
F: FnMut(H160, &[u8], Option<u64>, &Context) -> Option<PrecompileCallResult>,
{
// TODO use u64 for chain_id
let chain_id = Some(self.config.chain_id);
let chain_id = self.config.chain_id;

let tx_hash = tx.signing_hash(chain_id);
let unsigned_tx = UnsignedTransactionWithCaller {
unsigned_tx: tx.clone(),
caller,
chain_id,
signed_compatible: calculate_tx_hash_with_caller,
};
let tx_hash = unsigned_tx.tx_id_hash();
let result = self.transaction_execute_raw(
caller,
tx.nonce,
Expand All @@ -229,15 +235,10 @@ impl Executor {
tx.action,
tx.input.clone(),
tx.value,
chain_id,
Some(chain_id),
tx_hash,
precompiles,
)?;
let unsigned_tx = UnsignedTransactionWithCaller {
unsigned_tx: tx,
caller,
chain_id,
};

self.register_tx_with_receipt(TransactionInReceipt::Unsigned(unsigned_tx), result.clone());
Ok(result)
Expand Down Expand Up @@ -319,9 +320,7 @@ impl Executor {
fn register_tx_with_receipt(&mut self, tx: TransactionInReceipt, result: ExecutionResult) {
let tx_hash = match &tx {
TransactionInReceipt::Signed(tx) => tx.tx_id_hash(),
TransactionInReceipt::Unsigned(tx) => {
tx.unsigned_tx.signing_hash(Some(self.config.chain_id))
}
TransactionInReceipt::Unsigned(tx) => tx.tx_id_hash(),
};

debug!(
Expand Down Expand Up @@ -362,7 +361,13 @@ impl Executor {
);
}

pub fn register_swap_tx_in_evm(&mut self, mint_address: H160, recipient: H160, amount: U256) {
pub fn register_swap_tx_in_evm(
&mut self,
mint_address: H160,
recipient: H160,
amount: U256,
signed_compatible: bool,
) {
let nonce = self.with_executor(
|_, _, _, _| None,
|e| {
Expand All @@ -382,7 +387,8 @@ impl Executor {
let unsigned_tx = UnsignedTransactionWithCaller {
unsigned_tx: tx,
caller: mint_address,
chain_id: Some(self.config.chain_id),
chain_id: self.config.chain_id,
signed_compatible,
};
let result = ExecutionResult {
tx_logs: Vec::new(),
Expand Down Expand Up @@ -617,6 +623,7 @@ mod tests {
.transaction_execute_unsinged(
alice.secret.to_address(),
create_tx.clone(),
false,
noop_precompile
)
.unwrap()
Expand All @@ -627,12 +634,56 @@ mod tests {
let hash = create_tx.signing_hash(Some(chain_id));
assert!(matches!(
executor
.transaction_execute_unsinged(alice.secret.to_address(), create_tx, noop_precompile)
.transaction_execute_unsinged(alice.secret.to_address(), create_tx,false, noop_precompile)
.unwrap_err(),
Error::DuplicateTx { tx_hash } if tx_hash == hash
));
}

#[test]
fn handle_duplicate_txs_unsigned_new_hash() {
let _logger = simple_logger::SimpleLogger::new().init();

let chain_id = 0xeba;
let evm_config = EvmConfig {
chain_id,
..EvmConfig::default()
};
let mut executor =
Executor::with_config(EvmBackend::default(), Default::default(), evm_config);

let code = hex::decode(METACOIN_CODE).unwrap();

let alice = Persona::new();
let create_tx = alice.unsigned(TransactionAction::Create, &code);

let address = alice.address();
assert!(matches!(
executor
.transaction_execute_unsinged(address, create_tx.clone(), true, noop_precompile)
.unwrap()
.exit_reason,
ExitReason::Succeed(ExitSucceed::Returned)
));

let create_tx_with_caller = UnsignedTransactionWithCaller {
unsigned_tx: create_tx.clone(),
caller: address,
chain_id,
signed_compatible: true,
};
let hash = create_tx_with_caller.tx_id_hash();
println!("tx = {:?}", create_tx_with_caller);
println!("hash = {}", hash);

assert!(matches!(
executor
.transaction_execute_unsinged(address, create_tx, true, noop_precompile)
.unwrap_err(),
Error::DuplicateTx { tx_hash } if tx_hash == hash
));
}

#[test]
fn it_execute_only_txs_with_correct_chain_id() {
let _logger = simple_logger::SimpleLogger::new().init();
Expand Down
111 changes: 82 additions & 29 deletions evm-utils/evm-state/src/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub use secp256k1::{PublicKey, SecretKey, SECP256K1};
pub type Address = H160;
pub type Gas = U256;

const UNSIGNED_TX_MARKER: u8 = 0x1;

/// Etherium transaction.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Transaction {
Expand Down Expand Up @@ -364,8 +366,16 @@ impl Decodable for TransactionInReceipt {
fn decode(rlp: &Rlp<'_>) -> Result<Self, DecoderError> {
let items = rlp.item_count()?;
Ok(match items {
8 => TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode(rlp)?),
9 => TransactionInReceipt::Signed(Transaction::decode(rlp)?),
8 => TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode(rlp, false)?),
9 => {
if rlp.val_at::<u8>(8) == Ok(0x1u8) {
TransactionInReceipt::Unsigned(UnsignedTransactionWithCaller::decode(
rlp, true,
)?)
} else {
TransactionInReceipt::Signed(Transaction::decode(rlp)?)
}
}
_ => return Err(DecoderError::RlpInvalidLength),
})
}
Expand All @@ -375,39 +385,81 @@ impl Decodable for TransactionInReceipt {
pub struct UnsignedTransactionWithCaller {
pub unsigned_tx: UnsignedTransaction,
pub caller: H160,
pub chain_id: Option<u64>,
pub chain_id: u64,
// with signed_compatible, transaction serialization differ, and start to be compatible with signed tx, the only difference is that s is always empty.
pub signed_compatible: bool,
}

impl Encodable for UnsignedTransactionWithCaller {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(8);
s.append(&self.unsigned_tx.nonce);
s.append(&self.unsigned_tx.gas_price);
s.append(&self.unsigned_tx.gas_limit);
s.append(&self.unsigned_tx.action);
s.append(&self.unsigned_tx.value);
s.append(&self.unsigned_tx.input);
s.append(&self.caller);
s.append(&self.chain_id.unwrap_or(0));
let chain_id = self.chain_id;
if self.signed_compatible {
s.begin_list(9);
s.append(&self.unsigned_tx.nonce);
s.append(&self.unsigned_tx.gas_price);
s.append(&self.unsigned_tx.gas_limit);
s.append(&self.unsigned_tx.action);
s.append(&self.unsigned_tx.value);
s.append(&self.unsigned_tx.input);
s.append(&chain_id);
s.append(&self.caller);
s.append(&UNSIGNED_TX_MARKER);
} else {
s.begin_list(8);
s.append(&self.unsigned_tx.nonce);
s.append(&self.unsigned_tx.gas_price);
s.append(&self.unsigned_tx.gas_limit);
s.append(&self.unsigned_tx.action);
s.append(&self.unsigned_tx.value);
s.append(&self.unsigned_tx.input);
s.append(&self.caller);
s.append(&chain_id);
}
}
}

impl Decodable for UnsignedTransactionWithCaller {
fn decode(rlp: &Rlp<'_>) -> Result<Self, DecoderError> {
let chain_id = rlp.val_at(7)?;
let chain_id = if chain_id == 0 { None } else { Some(chain_id) };
Ok(Self {
unsigned_tx: UnsignedTransaction {
nonce: rlp.val_at(0)?,
gas_price: rlp.val_at(1)?,
gas_limit: rlp.val_at(2)?,
action: rlp.val_at(3)?,
value: rlp.val_at(4)?,
input: rlp.val_at(5)?,
},
caller: rlp.val_at(6)?,
chain_id,
})
impl UnsignedTransactionWithCaller {
pub fn tx_id_hash(&self) -> H256 {
// old transaction hash was calculated with different rlp structure, use signing_hash to be compatible
if !self.signed_compatible {
return self.unsigned_tx.signing_hash(Some(self.chain_id));
}
let mut stream = RlpStream::new();
self.rlp_append(&mut stream);
H256::from_slice(Keccak256::digest(&stream.as_raw()).as_slice())
}
fn decode(rlp: &Rlp<'_>, signed_compatible: bool) -> Result<Self, DecoderError> {
if signed_compatible {
let chain_id = rlp.val_at(6)?;
Ok(Self {
unsigned_tx: UnsignedTransaction {
nonce: rlp.val_at(0)?,
gas_price: rlp.val_at(1)?,
gas_limit: rlp.val_at(2)?,
action: rlp.val_at(3)?,
value: rlp.val_at(4)?,
input: rlp.val_at(5)?,
},
caller: rlp.val_at(7)?,
chain_id,
signed_compatible,
})
} else {
let chain_id = rlp.val_at(7)?;
Ok(Self {
unsigned_tx: UnsignedTransaction {
nonce: rlp.val_at(0)?,
gas_price: rlp.val_at(1)?,
gas_limit: rlp.val_at(2)?,
action: rlp.val_at(3)?,
value: rlp.val_at(4)?,
input: rlp.val_at(5)?,
},
caller: rlp.val_at(6)?,
chain_id,
signed_compatible,
})
}
}
}

Expand Down Expand Up @@ -577,7 +629,8 @@ mod test {
let unsigned = UnsignedTransactionWithCaller {
unsigned_tx,
caller: H160::repeat_byte(3),
chain_id: None,
chain_id: 0x0,
signed_compatible: false,
};
let bytes = rlp::encode(&unsigned);
let unsigned_deserialized: TransactionInReceipt =
Expand Down
6 changes: 6 additions & 0 deletions evm-utils/programs/evm_loader/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub enum EvmError {
#[snafu(display("Authorized transaction EVM address should be calculated from sender address using evm_address_for_program."))]
AuthorizedTransactionIncorrectAddress,

#[snafu(display("Wrong AuthorizedTx account owner.."))]
AuthorizedTransactionIncorrectOwner,

#[snafu(display("Cannot free ownership of an account that EVM didn't own."))]
FreeNotEvmAccount,

Expand All @@ -50,6 +53,9 @@ pub enum EvmError {

#[snafu(display("EVM Transaction was reverted."))]
RevertTransaction,

#[snafu(display("This instruction is not supported yet."))]
InstructionNotSupportedYet,
}

impl<E> DecodeError<E> for EvmError {
Expand Down
3 changes: 3 additions & 0 deletions evm-utils/programs/evm_loader/src/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub enum EvmBigTransaction {

/// Execute merged transaction, in order to do this, user should make sure that transaction is successfully writed.
EvmTransactionExecute {},

/// Execute merged unsigned transaction, in order to do this, user should make sure that transaction is successfully writed.
EvmTransactionExecuteUnsigned { from: evm::Address },
}

#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
Expand Down
Loading

0 comments on commit 5f5fa9a

Please sign in to comment.