conflux-rust
conflux-rust copied to clipboard
Implement EIP-2718 (Typed Transaction Envelope) for Both Core Space and eSpace
EIP-2718 should be implemented before EIP-1559 be implemented. This EIP can be accomplished for core space at the same time if more transaction types need to be introduced
Current Ethereum EIP-1559 design:
Raw transaction structure: 0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])
.
The signature_y_parity, signature_r, signature_s
elements of this transaction represent a secp256k1 signature over keccak256(0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list]))
What to do first: Refactor the transaction decoding: Currently, the transaction decoding is somewhat innatural due to some reason.
- transaction sent from cfx/eth_sendRawTransaction will be processed by the same decoder
- the transaction is decoded regardless of its source rpc, but depends on the transaction structure:
impl Decodable for TransactionWithSignatureSerializePart {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.item_count()? {
4 => {
let unsigned: NativeTransaction = rlp.val_at(0)?;
let v: u8 = rlp.val_at(1)?;
let r: U256 = rlp.val_at(2)?;
let s: U256 = rlp.val_at(3)?;
Ok(TransactionWithSignatureSerializePart {
unsigned: Transaction::Native(unsigned),
v,
r,
s,
})
}
9 => {
let nonce: U256 = rlp.val_at(0)?;
let gas_price: U256 = rlp.val_at(1)?;
let gas: U256 = rlp.val_at(2)?;
let action: Action = rlp.val_at(3)?;
let value: U256 = rlp.val_at(4)?;
let data: Vec<u8> = rlp.val_at(5)?;
let legacy_v: u64 = rlp.val_at(6)?;
let r: U256 = rlp.val_at(7)?;
let s: U256 = rlp.val_at(8)?;
let v = eip155_signature::extract_standard_v(legacy_v);
let chain_id =
match eip155_signature::extract_chain_id_from_legacy_v(
legacy_v,
) {
Some(chain_id) if chain_id > (u32::MAX as u64) => {
return Err(DecoderError::Custom(
"Does not support chain_id >= 2^32",
));
}
chain_id => chain_id.map(|x| x as u32),
};
Ok(TransactionWithSignatureSerializePart {
unsigned: Transaction::Ethereum(Eip155Transaction {
nonce,
gas_price,
gas,
action,
value,
chain_id,
data,
}),
v,
r,
s,
})
}
_ => Err(DecoderError::RlpInvalidLength),
}
}
}
- the rpc handler checks if the transaction space is as expected, and may reject the transaction if the decoded transaction type does not match
What is expected: Invoking decoder based on the handler
Espace and core space transactions will be packed in the same block, and it's still up to the decoder to decide the transaction type only based on the transaction structure. In this case, there seems no need to add another simpler decoder for each transaction type?
Data deserialization should always be independent of the system-level logic. So the decoder must be able to distinguish the core transaction and space transaction from the data structure only.
Data deserialization should always be independent of the system-level logic. So the decoder must be able to distinguish the core transaction and space transaction from the data structure only.
Espace and core space transactions will be packed in the same block, and it's still up to the decoder to decide the transaction type only based on the transaction structure. In this case, there seems no need to add another simpler decoder for each transaction type?
What about adding more spaces, for example, supposing another space is added and in the specific space it just simply reuses the ethereum transaction structure? Is it possible to distinguish the espace transaction and another space transaction without extra refactoring for the code?
What about adding more spaces, for example, supposing another space is added and in the specific space it just simply reuses the ethereum transaction structure? Is it possible to distinguish the espace transaction and another space transaction without extra refactoring for the code?
I think the main problem is that our espace transaction structure must be compatible with Eth transactions, which leaves us no room for customization. However, if we must use the same structure in another space for some reason, they can be distinguished by the chain id.
What about adding more spaces, for example, supposing another space is added and in the specific space it just simply reuses the ethereum transaction structure? Is it possible to distinguish the espace transaction and another space transaction without extra refactoring for the code?
I think the main problem is that our espace transaction structure must be compatible with Eth transactions, which leaves us no room for customization. However, if we must use the same structure in another space for some reason, they can be distinguished by the chain id.
Then that makes sense, in that way the space()
method implementation needs changing.
But a refactor might still be required. Currently, transaction decoded from rlp but from 2718, the transaction needs to be decoded from bytes
EIP1559 format:
0x02 || rlp([chain_id, nonce, max_priority_fee_per_gas, max_fee_per_gas, gas_limit, destination, amount, data, access_list, signature_y_parity, signature_r, signature_s])