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

Verify chunked txs #1618

Open
wants to merge 27 commits into
base: nightly
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
81039d6
Use anyhow
rakanalh Dec 17, 2024
4cec3cc
Add wxtid data to input / output
rakanalh Dec 17, 2024
7321775
Concatenate chunked proofs and verify
rakanalh Dec 17, 2024
43c911f
Store and pass wtxid_data
rakanalh Dec 17, 2024
073e092
Use flatten
rakanalh Dec 17, 2024
7958609
Fix tests
rakanalh Dec 17, 2024
fff8c1e
Use BTreeSet
rakanalh Dec 17, 2024
a0dcb68
Remove input field
rakanalh Dec 17, 2024
d3feecf
Remove field from storage
rakanalh Dec 17, 2024
0f4aac4
Revert change in da_block_handler
rakanalh Dec 17, 2024
0abc89e
Make wtxid_data mutable
rakanalh Dec 17, 2024
c7a562a
Move import
rakanalh Dec 18, 2024
3f49ea0
Add wtxids to aggregate
rakanalh Dec 20, 2024
8e98069
Pass wtxid to blob init
rakanalh Dec 20, 2024
91c2400
Verify chunk blob
rakanalh Dec 20, 2024
42b203a
Inject wtxid into blob
rakanalh Dec 20, 2024
e522194
fix test compilation
rakanalh Dec 20, 2024
64b9c58
Merge remote-tracking branch 'origin/nightly' into rakanalh/verify-ch…
rakanalh Dec 20, 2024
efc50a1
Fix problem with processing proof
rakanalh Dec 20, 2024
9c1e981
Oh my god, clippy!
rakanalh Dec 20, 2024
f0b2c4a
Get hash unverified
rakanalh Dec 20, 2024
6a07360
Rename wtxid_data to unprocessed_chunks
rakanalh Dec 20, 2024
68d1d14
Distinguish between txid and wtxid
rakanalh Dec 20, 2024
065212f
Check call result
rakanalh Dec 20, 2024
bae6c56
Store unprocessed chunks
rakanalh Dec 21, 2024
b75acbd
Merge remote-tracking branch 'origin/nightly' into rakanalh/verify-ch…
rakanalh Dec 21, 2024
72bebeb
Merge remote-tracking branch 'origin/nightly' into rakanalh/verify-ch…
rakanalh Dec 22, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,17 @@ pub fn create_inscription_type_1(
}
}

let reveal_tx_ids: Vec<_> = reveal_chunks
let (reveal_tx_ids, reveal_wtx_ids): (Vec<_>, Vec<_>) = reveal_chunks
.iter()
.map(|tx| tx.compute_txid().to_byte_array())
.map(|tx| {
(
tx.compute_txid().to_byte_array(),
tx.compute_wtxid().to_byte_array(),
)
})
.collect();

let aggregate = DaDataLightClient::Aggregate(reveal_tx_ids);
let aggregate = DaDataLightClient::Aggregate(reveal_tx_ids, reveal_wtx_ids);

// To sign the list of tx ids we assume they form a contigious list of bytes
let reveal_body: Vec<u8> =
Expand Down
12 changes: 12 additions & 0 deletions crates/bitcoin-da/src/helpers/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,18 @@ impl VerifyParsed for ParsedSequencerCommitment {
}
}

impl VerifyParsed for ParsedChunk {
fn public_key(&self) -> &[u8] {
&[0]
}
fn signature(&self) -> &[u8] {
&[0]
}
fn body(&self) -> &[u8] {
&self.body
}
}

#[derive(Error, Debug, Clone, PartialEq)]
pub enum ParserError {
#[error("Invalid header length")]
Expand Down
27 changes: 15 additions & 12 deletions crates/bitcoin-da/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bitcoin::hashes::Hash;
use bitcoin::secp256k1::SecretKey;
use bitcoin::{Amount, BlockHash, CompactTarget, Transaction, Txid, Wtxid};
use bitcoincore_rpc::json::{SignRawTransactionInput, TestMempoolAcceptResult};
use bitcoincore_rpc::{Auth, Client, Error, RpcApi, RpcError};
use bitcoincore_rpc::{Auth, Client, Error as BitcoinError, Error, RpcApi, RpcError};
use borsh::BorshDeserialize;
use citrea_primitives::compression::{compress_blob, decompress_blob};
use citrea_primitives::MAX_TXBODY_SIZE;
Expand Down Expand Up @@ -774,7 +774,7 @@ impl DaService for BitcoinService {
let mut body = Vec::new();
let data = DaDataLightClient::try_from_slice(&aggregate.body)
.map_err(|e| anyhow!("{}: Failed to parse aggregate: {e}", tx_id))?;
let DaDataLightClient::Aggregate(chunk_ids) = data else {
let DaDataLightClient::Aggregate(chunk_ids, _wtx_ids) = data else {
error!("{}: Aggregate: unexpected kind", tx_id);
continue;
};
Expand All @@ -790,12 +790,9 @@ impl DaService for BitcoinService {
self.client
.get_raw_transaction(&chunk_id, None)
.await
.map_err(|e| {
use bitcoincore_rpc::Error;
match e {
Error::Io(_) => backoff::Error::transient(e),
_ => backoff::Error::permanent(e),
}
.map_err(|e| match e {
BitcoinError::Io(_) => backoff::Error::transient(e),
_ => backoff::Error::permanent(e),
})
})
.await;
Expand Down Expand Up @@ -948,6 +945,7 @@ impl DaService for BitcoinService {

let mut relevant_txs = vec![];
for tx in &completeness_proof {
let wtxid = tx.compute_wtxid();
match namespace {
DaNamespace::ToBatchProver => {
if let Ok(tx) = parse_batch_proof_transaction(tx) {
Expand All @@ -958,6 +956,7 @@ impl DaService for BitcoinService {
seq_comm.body,
seq_comm.public_key,
hash,
None,
);

relevant_txs.push(relevant_tx);
Expand All @@ -973,7 +972,7 @@ impl DaService for BitcoinService {
if let Some(hash) = complete.get_sig_verified_hash() {
let blob = decompress_blob(&complete.body);
let relevant_tx =
BlobWithSender::new(blob, complete.public_key, hash);
BlobWithSender::new(blob, complete.public_key, hash, None);

relevant_txs.push(relevant_tx);
}
Expand All @@ -984,13 +983,17 @@ impl DaService for BitcoinService {
aggregate.body,
aggregate.public_key,
hash,
None,
);

relevant_txs.push(relevant_tx);
}
}
ParsedLightClientTransaction::Chunk(_) => {
// ignore
ParsedLightClientTransaction::Chunk(chunk) => {
let mut relevant_tx =
BlobWithSender::new(chunk.body, vec![0], [0; 32], None);
relevant_tx.wtxid = Some(wtxid.to_byte_array());
relevant_txs.push(relevant_tx);
}
}
}
Expand Down Expand Up @@ -1128,7 +1131,7 @@ pub fn get_relevant_blobs_from_txs(
ParsedBatchProofTransaction::SequencerCommitment(seq_comm) => {
if let Some(hash) = seq_comm.get_sig_verified_hash() {
let relevant_tx =
BlobWithSender::new(seq_comm.body, seq_comm.public_key, hash);
BlobWithSender::new(seq_comm.body, seq_comm.public_key, hash, None);

relevant_txs.push(relevant_tx);
}
Expand Down
29 changes: 18 additions & 11 deletions crates/bitcoin-da/src/spec/blob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,28 @@ pub struct BlobBuf {
pub offset: usize,
}

// BlobWithSender is a wrapper around BlobBuf to implement BlobReaderTrait
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct BlobWithSender {
pub hash: [u8; 32],

pub sender: AddressWrapper,

pub blob: CountedBufReader<BlobBuf>,

pub wtxid: Option<[u8; 32]>,
}

impl BlobWithSender {
pub fn new(blob: Vec<u8>, sender: Vec<u8>, hash: [u8; 32]) -> Self {
pub fn new(blob: Vec<u8>, sender: Vec<u8>, hash: [u8; 32], wtxid: Option<[u8; 32]>) -> Self {
Self {
blob: CountedBufReader::new(BlobBuf {
data: blob,
offset: 0,
}),
sender: AddressWrapper(sender),
hash,
wtxid,
}
}
}
Expand All @@ -40,16 +53,6 @@ impl Buf for BlobBuf {
}
}

// BlobWithSender is a wrapper around BlobBuf to implement BlobReaderTrait
#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize, Serialize, Deserialize)]
pub struct BlobWithSender {
pub hash: [u8; 32],

pub sender: AddressWrapper,

pub blob: CountedBufReader<BlobBuf>,
}

impl BlobReaderTrait for BlobWithSender {
type Address = AddressWrapper;

Expand All @@ -61,6 +64,10 @@ impl BlobReaderTrait for BlobWithSender {
self.hash
}

fn wtxid(&self) -> Option<[u8; 32]> {
self.wtxid
}

fn verified_data(&self) -> &[u8] {
self.blob.accumulator()
}
Expand Down
39 changes: 37 additions & 2 deletions crates/bitcoin-da/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,14 @@ impl DaVerifier for BitcoinVerifier {
}
}
}
ParsedLightClientTransaction::Chunk(_chunk) => {
// ignore
ParsedLightClientTransaction::Chunk(chunk) => {
if let Some(blob_content) =
verified_blob_chunk(&chunk, &mut blobs_iter, wtxid)?
{
if blob_content != chunk.body {
return Err(ValidationError::BlobContentWasModified);
}
}
}
}
}
Expand Down Expand Up @@ -307,6 +313,35 @@ impl DaVerifier for BitcoinVerifier {
}
}

fn verified_blob_chunk<'a, T, I>(
tx: &T,
blobs_iter: &mut I,
wtxid: &[u8; 32],
) -> Result<Option<&'a [u8]>, ValidationError>
where
T: VerifyParsed,
I: Iterator<Item = &'a BlobWithSender>,
{
if let Some(blob_hash) = tx.get_sig_verified_hash() {
rakanalh marked this conversation as resolved.
Show resolved Hide resolved
let blob = blobs_iter.next();

let Some(blob) = blob else {
return Err(ValidationError::ValidBlobNotFoundInBlobs);
};

if blob.hash != blob_hash {
return Err(ValidationError::BlobWasTamperedWith);
}

if blob.wtxid != Some(*wtxid) {
return Err(ValidationError::BlobWasTamperedWith);
}

return Ok(Some(blob.verified_data()));
}
Ok(None)
}

// Get associated blob content only if signatures, hashes and public keys match
fn verified_blob_content<'a, T, I>(
tx: &T,
Expand Down
2 changes: 1 addition & 1 deletion crates/bitcoin-da/tests/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ pub fn get_blob_with_sender(tx: &Transaction, ty: MockData) -> anyhow::Result<Bl
}
};

Ok(BlobWithSender::new(blob.clone(), public_key, hash))
Ok(BlobWithSender::new(blob.clone(), public_key, hash, None))
}

// For some reason, even though macro is used, it sees it as unused
Expand Down
12 changes: 7 additions & 5 deletions crates/bitcoin-da/tests/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ impl TestCase for BitcoinVerifierTest {
{
let mut b_txs = b_txs.clone();

b_txs[0] = BlobWithSender::new(vec![2; 152], b_txs[0].sender.0.clone(), b_txs[0].hash);
b_txs[0] =
BlobWithSender::new(vec![2; 152], b_txs[0].sender.0.clone(), b_txs[0].hash, None);
assert_eq!(
verifier.verify_transactions(
&block.header,
Expand All @@ -520,7 +521,8 @@ impl TestCase for BitcoinVerifierTest {
{
let mut l_txs = l_txs.clone();

l_txs[0] = BlobWithSender::new(vec![2; 152], l_txs[0].sender.0.clone(), l_txs[0].hash);
l_txs[0] =
BlobWithSender::new(vec![2; 152], l_txs[0].sender.0.clone(), l_txs[0].hash, None);
assert_eq!(
verifier.verify_transactions(
&block.header,
Expand All @@ -541,7 +543,7 @@ impl TestCase for BitcoinVerifierTest {
blob.advance(blob.total_len());
let blob = blob.accumulator().to_vec();

b_txs[0] = BlobWithSender::new(blob, vec![2; 33], b_txs[0].hash);
b_txs[0] = BlobWithSender::new(blob, vec![2; 33], b_txs[0].hash, None);
assert_eq!(
verifier.verify_transactions(
&block.header,
Expand All @@ -562,7 +564,7 @@ impl TestCase for BitcoinVerifierTest {
blob.advance(blob.total_len());
let blob = blob.accumulator().to_vec();

l_txs[0] = BlobWithSender::new(blob, vec![2; 33], l_txs[0].hash);
l_txs[0] = BlobWithSender::new(blob, vec![2; 33], l_txs[0].hash, None);
assert_eq!(
verifier.verify_transactions(
&block.header,
Expand All @@ -587,7 +589,7 @@ impl TestCase for BitcoinVerifierTest {
}
};

l_txs[0] = BlobWithSender::new(body, l_txs[0].sender.0.clone(), l_txs[0].hash);
l_txs[0] = BlobWithSender::new(body, l_txs[0].sender.0.clone(), l_txs[0].hash, None);
assert_eq!(
verifier.verify_transactions(
&block.header,
Expand Down
3 changes: 1 addition & 2 deletions crates/light-client-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ sov-stf-runner = { path = "../sovereign-sdk/full-node/sov-stf-runner", optional

# 3rd-party deps
alloy-primitives = { workspace = true, optional = true }
anyhow = { workspace = true, optional = true }
anyhow = { workspace = true }
async-trait = { workspace = true, optional = true }
bincode = { workspace = true }
borsh = { workspace = true }
Expand Down Expand Up @@ -50,7 +50,6 @@ native = [
"dep:sov-db",
"dep:sov-stf-runner",
"dep:sov-ledger-rpc",
"dep:anyhow",
"dep:async-trait",
"dep:jsonrpsee",
"dep:metrics",
Expand Down
Loading
Loading