Skip to content

Commit

Permalink
[state-sync-v2] change TxnOutputListWithProof to include transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
lightmark authored and bors-libra committed Oct 22, 2021
1 parent fab8933 commit 1579945
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 26 deletions.
2 changes: 1 addition & 1 deletion storage/diemdb/src/diemdb_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ fn verify_committed_transactions(
txn_output_list_with_proof
.verify(ledger_info, Some(cur_ver))
.unwrap();
assert_eq!(txn_output_list_with_proof.transaction_outputs.len(), 1);
assert_eq!(txn_output_list_with_proof.transactions_and_outputs.len(), 1);

// Fetch and verify account states.
for (addr, expected_blob) in txn_to_commit.account_states() {
Expand Down
7 changes: 4 additions & 3 deletions storage/diemdb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,18 +812,19 @@ impl DbReader<DpnProto> for DiemDB {

let limit = std::cmp::min(limit, ledger_version - start_version + 1);

let (txn_infos, txn_outputs) = (start_version..start_version + limit)
let (txn_infos, txns_and_outputs) = (start_version..start_version + limit)
.map(|version| {
let txn_info = self.ledger_store.get_transaction_info(version)?;
let events = self.event_store.get_events_by_version(version)?;
let write_set = self.transaction_store.get_write_set(version)?;
let txn = self.transaction_store.get_transaction(version)?;
let txn_output = TransactionOutput::new(
write_set,
events,
txn_info.gas_used(),
txn_info.status().clone().into(),
);
Ok((txn_info, txn_output))
Ok((txn_info, (txn, txn_output)))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
Expand All @@ -838,7 +839,7 @@ impl DbReader<DpnProto> for DiemDB {
);

Ok(TransactionOutputListWithProof::new(
txn_outputs,
txns_and_outputs,
Some(start_version),
proof,
))
Expand Down
26 changes: 17 additions & 9 deletions types/src/proof/unit_tests/proof_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,20 +619,28 @@ fn test_transaction_list_with_proof() {
}

#[test]
fn test_transaction_output_list_with_proof() {
// Create test event and transaction output
fn test_transaction_and_output_list_with_proof() {
// Create test transaction, event and transaction output
let transaction = Transaction::BlockMetadata(BlockMetadata::new(
HashValue::random(),
0,
0,
vec![],
AccountAddress::random(),
));
let event = create_event();
let transaction_output = TransactionOutput::new(
WriteSet::default(),
vec![event.clone()],
0,
TransactionStatus::Retry,
TransactionStatus::Keep(KeptVMStatus::MiscellaneousError),
);

// Create transaction output list with proof
let transaction_info_list_proof = create_single_transaction_info_proof(None, None);
let transaction_info_list_proof =
create_single_transaction_info_proof(Some(transaction.hash()), None);
let transaction_output_list_proof = TransactionOutputListWithProof::new(
vec![transaction_output.clone()],
vec![(transaction.clone(), transaction_output.clone())],
Some(1),
transaction_info_list_proof.clone(),
);
Expand All @@ -653,18 +661,18 @@ fn test_transaction_output_list_with_proof() {

// Construct a new transaction output list proof where the transaction info and event hashes match
let transaction_info_list_proof =
create_single_transaction_info_proof(None, Some(event.hash()));
create_single_transaction_info_proof(Some(transaction.hash()), Some(event.hash()));
let expected_info_hash = transaction_info_list_proof.transaction_infos[0].hash();
let transaction_output_list_proof = TransactionOutputListWithProof::new(
vec![transaction_output],
let transaction_and_output_list_proof = TransactionOutputListWithProof::new(
vec![(transaction, transaction_output)],
Some(1),
transaction_info_list_proof,
);

// Ensure ledger verification now passes
let block_info = BlockInfo::new(0, 0, HashValue::random(), expected_info_hash, 0, 0, None);
let ledger_info = LedgerInfo::new(block_info, HashValue::zero());
transaction_output_list_proof
transaction_and_output_list_proof
.verify(&ledger_info, Some(1))
.unwrap();
}
Expand Down
70 changes: 57 additions & 13 deletions types/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,19 +1192,19 @@ impl<T: TransactionInfoTrait> TransactionListWithProof<T> {
/// resulting state matches the expected state in the proof (for each version).
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct TransactionOutputListWithProof<T> {
pub transaction_outputs: Vec<TransactionOutput>,
pub transactions_and_outputs: Vec<(Transaction, TransactionOutput)>,
pub first_transaction_output_version: Option<Version>,
pub proof: TransactionInfoListWithProof<T>,
}

impl<T: TransactionInfoTrait> TransactionOutputListWithProof<T> {
pub fn new(
transaction_outputs: Vec<TransactionOutput>,
transactions_and_outputs: Vec<(Transaction, TransactionOutput)>,
first_transaction_output_version: Option<Version>,
proof: TransactionInfoListWithProof<T>,
) -> Self {
Self {
transaction_outputs,
transactions_and_outputs,
first_transaction_output_version,
proof,
}
Expand All @@ -1220,7 +1220,9 @@ impl<T: TransactionInfoTrait> TransactionOutputListWithProof<T> {
/// 1. All transaction infos exist on the given `ledger_info`.
/// 2. If `first_transaction_output_version` is None, the transaction output list is empty.
/// Otherwise, the list starts at `first_transaction_output_version`.
/// 3. Events in each transaction output match the expected event root hashes in the proof.
/// 3. Events, gas, status in each transaction output match the expected event root hashes,
/// the gas used and the transaction execution status in the proof, respectively.
/// 4. The transaction hashes match those of the transaction infos.
///
/// Note: the proof cannot verify the TransactionOutputs themselves. This
/// requires speculative execution of each TransactionOutput to verify that the
Expand All @@ -1230,25 +1232,67 @@ impl<T: TransactionInfoTrait> TransactionOutputListWithProof<T> {
ledger_info: &LedgerInfo,
first_transaction_output_version: Option<Version>,
) -> Result<()> {
// Verify the first transaction output versions match
// Verify the first transaction/output versions match
ensure!(
self.first_transaction_output_version == first_transaction_output_version,
"First transaction output version ({:?}) doesn't match given version ({:?}).",
"First transaction and output version ({:?}) doesn't match given version ({:?}).",
self.first_transaction_output_version,
first_transaction_output_version,
);

// Verify the lengths of the transaction(output)s and transaction infos match
ensure!(
self.proof.transaction_infos.len() == self.transactions_and_outputs.len(),
"The number of TransactionInfo objects ({}) does not match the number of \
transactions and outputs ({}).",
self.proof.transaction_infos.len(),
self.transactions_and_outputs.len(),
);

// Verify the events, status, gas used and transaction hashes.
itertools::zip_eq(
&self.transactions_and_outputs,
&self.proof.transaction_infos,
)
.map(|((txn, txn_output), txn_info)| {
// Check the events against the expected events root hash
verify_events_against_root_hash(&txn_output.events, txn_info)?;

// Verify the gas matches for both the transaction info and output
ensure!(
txn_output.gas_used() == txn_info.gas_used(),
"The gas used in transaction output does not match the transaction info \
in proof. Gas used in transaction output: {}. Gas used in txn_info: {}.",
txn_output.gas_used(),
txn_info.gas_used(),
);

// Verify the execution status matches for both the transaction info and output.
ensure!(
*txn_output.status() == TransactionStatus::Keep(txn_info.status().clone()),
"The execution status of transaction output does not match the transaction \
info in proof. Status in transaction output: {:?}. Status in txn_info: {:?}.",
txn_output.status(),
txn_info.status(),
);

// Verify the transaction hashes match those of the transaction infos
let txn_hash = txn.hash();
ensure!(
txn_hash == txn_info.transaction_hash(),
"The transaction hash does not match the hash in transaction info. \
Transaction hash: {:x}. Transaction hash in txn_info: {:x}.",
txn_hash,
txn_info.transaction_hash(),
);
Ok(())
})
.collect::<Result<Vec<_>>>()?;

// Verify the transaction infos are proven by the ledger info.
self.proof
.verify(ledger_info, self.first_transaction_output_version)?;

// Verify the events
itertools::zip_eq(&self.transaction_outputs, &self.proof.transaction_infos)
.map(|(txn_output, txn_info)| {
verify_events_against_root_hash(&txn_output.events, txn_info)
})
.collect::<Result<Vec<_>>>()?;

Ok(())
}
}
Expand Down

0 comments on commit 1579945

Please sign in to comment.