Skip to content

Comments

Audit receipts + CTS implementation#14

Open
task3r wants to merge 104 commits intomainfrom
audit-receipts
Open

Audit receipts + CTS implementation#14
task3r wants to merge 104 commits intomainfrom
audit-receipts

Conversation

@task3r
Copy link
Collaborator

@task3r task3r commented Jul 11, 2025

No description provided.

@task3r task3r changed the title [WIP] Audit receipts [WIP] Audit receipts + CTS implementation Aug 22, 2025
@task3r task3r marked this pull request as ready for review August 22, 2025 12:24
@task3r
Copy link
Collaborator Author

task3r commented Aug 22, 2025

If we disable merkle tree, receipts and policy validation, we get basically the same perf as previously (running on C-ACI, for the reasons we discussed).

image

@task3r task3r changed the title [WIP] Audit receipts + CTS implementation Audit receipts + CTS implementation Aug 22, 2025
}

fn validate_qcs(
chain: &Vec<ProtoBlock>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having the receipt chain be the whole blocks seems expensive.
(Not necessarily something that needs fixed, but probably a good place to look for optimisations)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the rest of this function it might be sufficient to just have the merkle inclusion proof of the previous chained block and the hash?

Copy link
Collaborator Author

@task3r task3r Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A ProtoBlock is just a ref, not the actual block Sorry, I misrembered. Nevertheless, I though you were worried about copying and not the actual receipt structure. We can discuss this with Shubham next time we meet, but he was explicit that we could not simply use the hash of the txs (in this case the merkle root) to prove chain consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, but the concern is that this is a lot of copying of reasonably large amounts of memory for every transaction in the scitt demo.

) -> Result<(), String> {
let ctx = Context::full(runtime).unwrap();
ctx.with::<_, Result<(), String>>(|ctx| {
if let Err(err) = ctx.eval::<(), _>(policy).catch(&ctx) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible optimisation: Cache the runtime with the policy evaluated.


let globals = ctx.globals();
let apply_fn: Function = globals.get("apply").unwrap();
let result: Result<Value, _> = apply_fn.call((phdr_arg,));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let result: Result<Value, _> = apply_fn.call((phdr_arg,));
let result: Result<Value, _> = apply_fn.call((phdr_arg));

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is needed for type matching.

error[E0277]: the trait bound `rquickjs::Object<'_>: IntoArgs<'_>` is not satisfied
   --> src/consensus/engines/scitt.rs:529:54
    |
529 |         let result: Result<Value, _> = apply_fn.call((phdr_arg));
    |                                                 ---- ^^^^^^^^^^ the trait `IntoArgs<'_>` is not implemented for `rquickjs::Object<'_>`
    |                                                 |
    |                                                 required by a bound introduced by this call
    |
    = help: the following other types implement trait `IntoArgs<'js>`:
              ()
              (A, B)
              (A, B, C)
              (A, B, C, D)
              (A, B, C, D, E)
              (A, B, C, D, E, F)
              (A, B, C, D, E, F, G)
              (A,)

return;
}

// warn!("Byzantine commit from {} to {}. QC {}", old_bci, new_bci, qc.n);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tidy



impl<'a, E: AppEngine + Send + Sync + 'a> Application<'a, E> {
impl<'a, E: AppEngine + Send + Sync + 'a + 'static> Application<'a, E> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is static lifetime necessary here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on this. If the compiler complains about AppEngine not living long enough, suggest setting where 'a: 'static

return;
}

#[allow(unused_mut)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surely this is unnecessary?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing to:

let results = {
     #[cfg(feature = "concurrent_validation")]
     self.engine.write().await.handle_byz_commit(blocks)

     #[cfg(not(feature = "concurrent_validation"))]
     self.engine.handle_byz_commit(blocks)
};

This is more idiomatic in rust.

Ok(())
}

#[allow(unused_mut)] // ignore warning for unused muts (needed for policy validation, otherwise we would need to copy stuff around needlessly)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better solution (inverse of a COW or something?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean like when the batch.clone() is sent to the app?
Probably can pin the RawBatch in heap and just pass a pointer.
(Ref to how CachedBlock works)

pub type ProofChain = Vec<ProtoBlock>;
pub struct ReceiptBuilder {
// The chain of blocks from the requested block to the subsequent auditQC
pub chain: ProofChain,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned elsewhere, this seems very expensive for every receipt.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest changing to

pub type ProofChain = Vec<CachedBlock>;

CachedBlock is pinned in the heap. Vec is in effect just Vec of pointers, instead of here where there is a bunch of memcpy involved potentially.

crypto::{default_hash, hash, HashType, Sha},
proto::consensus::ProtoBlock, utils::unwrap_tx_list,
};

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you have searched for this, but to double check that there wasn't a good pre-existing merkle proof library?

impl MerkleTree {
pub fn new(leaves: Vec<HashType>) -> Self {
let n_leaves = leaves.len();
let n_padded_leaves = n_leaves.next_power_of_two();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is possibly expensive, but in practise afaicr the blocks are guaranteed to be a power of 2 already?

The alternative is to push the overhanging portion up the tree:

ie [0,1,2,3,4] is hashed H(H(H(0,1),H(2,3)), 4) rather than H(H(0,1),H(2,3)),H(H(4,0),H(0,0)))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 It is more standard to push the nodes up the tree.

MerkleInclusionProof::new(proof)
}

/// benchmarks show that this is actually slightly slower than calling `generate_inclusion_proof` N times... left it for future reference
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something something vectorisation something something :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crypto service.rs is already quite crowded. But you can use crypto service to do things in parallel if you want.
Not guaranteed to speed up anything. :-)

@grapheo12
Copy link
Collaborator

@task3r Left some meta comments in reply to the @cjen1-msft 's comments. Let me know if I can be of any help.

* fix formatting
* remove static lifetime from app
* remove padding from merkle proofs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants