Skip to content
Closed
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
6 changes: 5 additions & 1 deletion crates/protocol/src/batch/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ impl RawSpanBatch {
}
}

// Recover `v` values in transaction signatures within the batch.
self.payload.txs.recover_v(chain_id)?;

// Get all transactions in the batch.
let enveloped_txs = self.payload.txs.full_txs(chain_id)?;

Expand Down Expand Up @@ -162,7 +165,8 @@ mod test {
fn test_decode_encode_raw_span_batch() {
// Load in the raw span batch from the `op-node` derivation pipeline implementation.
let raw_span_batch_hex = include_bytes!("./testdata/raw_batch.hex");
let raw_span_batch = RawSpanBatch::decode(&mut raw_span_batch_hex.as_slice()).unwrap();
let mut raw_span_batch = RawSpanBatch::decode(&mut raw_span_batch_hex.as_slice()).unwrap();
raw_span_batch.payload.txs.recover_v(981).unwrap();

let mut encoding_buf = Vec::new();
raw_span_batch.encode(&mut encoding_buf).unwrap();
Expand Down
66 changes: 52 additions & 14 deletions crates/protocol/src/batch/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct SpanBatchTransactions {
pub total_block_tx_count: u64,
/// The contract creation bits, standard span-batch bitlist.
pub contract_creation_bits: SpanBatchBits,
/// The y parity bits, standard span-batch bitlist.
pub y_parity_bits: SpanBatchBits,
/// The transaction signatures.
pub tx_sigs: Vec<Signature>,
/// The transaction nonces
Expand All @@ -40,7 +42,8 @@ impl SpanBatchTransactions {
/// Encodes the [SpanBatchTransactions] into a writer.
pub fn encode(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
self.encode_contract_creation_bits(w)?;
self.encode_tx_sigs(w)?;
self.encode_y_parity_bits(w)?;
self.encode_tx_sigs_rs(w)?;
self.encode_tx_tos(w)?;
self.encode_tx_datas(w)?;
self.encode_tx_nonces(w)?;
Expand All @@ -52,7 +55,8 @@ impl SpanBatchTransactions {
/// Decodes the [SpanBatchTransactions] from a reader.
pub fn decode(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
self.decode_contract_creation_bits(r)?;
self.decode_tx_sigs(r)?;
self.decode_y_parity_bits(r)?;
self.decode_tx_sigs_rs(r)?;
self.decode_tx_tos(r)?;
self.decode_tx_datas(r)?;
self.decode_tx_nonces(r)?;
Expand All @@ -73,14 +77,14 @@ impl SpanBatchTransactions {
Ok(())
}

/// Encode the transaction signatures into a writer (excluding `v` field).
pub fn encode_tx_sigs(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
let mut y_parity_bits = SpanBatchBits::default();
for (i, sig) in self.tx_sigs.iter().enumerate() {
y_parity_bits.set_bit(i, sig.v());
}
/// Encode the y parity bits into a writer.
pub fn encode_y_parity_bits(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
SpanBatchBits::encode(w, self.total_block_tx_count as usize, &self.y_parity_bits)?;
Ok(())
}

SpanBatchBits::encode(w, self.total_block_tx_count as usize, &y_parity_bits)?;
/// Encode the transaction signatures into a writer (excluding `v` field).
pub fn encode_tx_sigs_rs(&self, w: &mut Vec<u8>) -> Result<(), SpanBatchError> {
for sig in &self.tx_sigs {
w.extend_from_slice(&sig.r().to_be_bytes::<32>());
w.extend_from_slice(&sig.s().to_be_bytes::<32>());
Expand Down Expand Up @@ -144,15 +148,19 @@ impl SpanBatchTransactions {
Ok(())
}

/// Decode the y parity bits from a reader.
pub fn decode_y_parity_bits(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
self.y_parity_bits = SpanBatchBits::decode(r, self.total_block_tx_count as usize)?;
Ok(())
}

/// Decode the transaction signatures from a reader (excluding `v` field).
pub fn decode_tx_sigs(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
let y_parity_bits = SpanBatchBits::decode(r, self.total_block_tx_count as usize)?;
pub fn decode_tx_sigs_rs(&mut self, r: &mut &[u8]) -> Result<(), SpanBatchError> {
let mut sigs = Vec::with_capacity(self.total_block_tx_count as usize);
for i in 0..self.total_block_tx_count {
let y_parity = y_parity_bits.get_bit(i as usize).expect("same length");
for _ in 0..self.total_block_tx_count {
let r_val = U256::from_be_slice(&r[..32]);
let s_val = U256::from_be_slice(&r[32..64]);
sigs.push(Signature::new(r_val, s_val, y_parity == 1));
sigs.push(Signature::new(r_val, s_val, false));
r.advance(64);
}
self.tx_sigs = sigs;
Expand Down Expand Up @@ -225,6 +233,34 @@ impl SpanBatchTransactions {
self.contract_creation_bits.as_ref().iter().map(|b| b.count_ones() as u64).sum()
}

/// Recover the `v` values of the transaction signatures.
pub fn recover_v(&mut self, chain_id: u64) -> Result<(), SpanBatchError> {
if self.tx_sigs.len() != self.tx_types.len() {
return Err(SpanBatchError::Decoding(SpanDecodingError::TypeSignatureLenMismatch));
}
let mut protected_bits_idx = 0;
for (i, tx_type) in self.tx_types.iter().enumerate() {
let bit = self.y_parity_bits.get_bit(i).ok_or(SpanBatchError::BitfieldTooLong)?;
let v = match tx_type {
TxType::Legacy => {
// Legacy transaction
let protected_bit = self.protected_bits.get_bit(protected_bits_idx);
protected_bits_idx += 1;
if protected_bit.is_none() || protected_bit.is_some_and(|b| b == 0) {
Ok(27 + bit as u64)
} else {
// EIP-155
Ok(chain_id * 2 + 35 + bit as u64)
}
}
TxType::Eip2930 | TxType::Eip1559 => Ok(bit as u64),
_ => Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)),
}?;
self.tx_sigs[i] = Signature::new(self.tx_sigs[i].r(), self.tx_sigs[i].s(), v == 1);
}
Ok(())
}

/// Retrieve all of the raw transactions from the [SpanBatchTransactions].
pub fn full_txs(&self, chain_id: u64) -> Result<Vec<Vec<u8>>, SpanBatchError> {
let mut txs = Vec::new();
Expand Down Expand Up @@ -312,6 +348,7 @@ impl SpanBatchTransactions {
return Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData));
}

let y_parity = signature.v();
let contract_creation_bit = match to {
Some(address) => {
self.tx_tos.push(address);
Expand All @@ -324,6 +361,7 @@ impl SpanBatchTransactions {

self.tx_sigs.push(*signature);
self.contract_creation_bits.set_bit((i + offset) as usize, contract_creation_bit == 1);
self.y_parity_bits.set_bit((i + offset) as usize, y_parity);
self.tx_nonces.push(nonce);
self.tx_datas.push(tx_data_buf);
self.tx_gases.push(gas);
Expand Down