Skip to content

Commit f20753a

Browse files
authored
feat(avm): defensively assert cd hashes (#19346)
This PR moves us from `compute_calldata_hash` -> `assert_calldata_hash` which essentially adds a degree of validation to the calldata hash in the AVM cpp code. ### Why tho? 1. Aligns it with other "infallible" pre-requisites to proving, e.g., `assert_address_derivation`. 2. Somewhat minimises (although not completely) the following prover DOS vector. - A sequencer generates proving hints with a bad calldata hash (i.e. `H(calldata) != calldata_hash`) - Gives the hints to a prover agent, since there is no validation the prover agent constructs the entire trace. - The proof construction fails because the lookup constraining calldata hash is invalid, all the prover work is wasted 3. It only minimises the attack vector because the prover agent still wastes some work during re-execution, although this should be significantly less than when it gets to tracegen. ### Sharp Edges ~In TS, cd hash validation is done at the tx validation level. I don't think it is worthwhile to implement the cd hash validation in the TS simulator - so instead I've implemented a validation in the TS fuzzer entrypoint~
1 parent 99cd00c commit f20753a

File tree

11 files changed

+20
-12
lines changed

11 files changed

+20
-12
lines changed

barretenberg/cpp/src/barretenberg/avm_fuzzer/harness/calldata.fuzzer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "barretenberg/vm2/simulation/events/event_emitter.hpp"
2020
#include "barretenberg/vm2/simulation/gadgets/calldata_hashing.hpp"
2121
#include "barretenberg/vm2/simulation/interfaces/calldata_hashing.hpp"
22+
#include "barretenberg/vm2/simulation/lib/contract_crypto.hpp"
2223
#include "barretenberg/vm2/tooling/debugger.hpp"
2324
#include "barretenberg/vm2/tracegen/calldata_trace.hpp"
2425
#include "barretenberg/vm2/tracegen/execution_trace.hpp"
@@ -363,7 +364,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
363364
try {
364365
for (size_t i = 0; i < num_events; i++) {
365366
auto calldata_interface = calldata_hashing_provider.make_calldata_hasher(context_id++);
366-
calldata_interface->compute_calldata_hash(calldata_fields[i]);
367+
FF cd_hash = compute_calldata_hash(calldata_fields[i]);
368+
calldata_interface->assert_calldata_hash(cd_hash, calldata_fields[i]);
367369
}
368370
} catch (const std::exception& e) {
369371
// If any exception occurs, we cannot proceed further.

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/calldata_hashing.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66

77
namespace bb::avm2::simulation {
88

9-
FF CalldataHasher::compute_calldata_hash(std::span<const FF> calldata)
9+
void CalldataHasher::assert_calldata_hash(const FF& cd_hash, std::span<const FF> calldata)
1010
{
1111
// todo(ilyas): this probably simulates faster at the cost of re-work in tracegen
1212
std::vector<FF> calldata_with_sep = { DOM_SEP__PUBLIC_CALLDATA };
1313
for (const auto& value : calldata) {
1414
// Note: Using `insert` breaks GCC.
1515
calldata_with_sep.push_back(value);
1616
}
17-
FF output_hash = hasher.hash(calldata_with_sep);
17+
FF computed_hash = hasher.hash(calldata_with_sep);
18+
BB_ASSERT_EQ(computed_hash, cd_hash);
1819

1920
events.emit({
2021
.context_id = context_id,
2122
.calldata = { calldata.begin(), calldata.end() },
2223
});
23-
return output_hash;
2424
}
2525

2626
} // namespace bb::avm2::simulation

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/calldata_hashing.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class CalldataHasher : public CalldataHashingInterface {
1818
, hasher(hasher)
1919
{}
2020

21-
FF compute_calldata_hash(std::span<const FF> calldata) override;
21+
void assert_calldata_hash(const FF& cd_hash, std::span<const FF> calldata) override;
2222

2323
private:
2424
uint32_t context_id;

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/calldata_hashing.test.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ TEST_F(CalldataHashingTest, SimpleHash)
5959

6060
EXPECT_CALL(poseidon2, hash(prepended_calldata_fields)).WillOnce(Return(hash));
6161

62-
auto output_hash = calldata_hasher.compute_calldata_hash(calldata_fields);
62+
calldata_hasher.assert_calldata_hash(hash, calldata_fields);
6363

64-
EXPECT_EQ(output_hash, hash);
6564
EXPECT_THAT(
6665
calldata_events.dump_events(),
6766
AllOf(SizeIs(1),
@@ -77,7 +76,7 @@ TEST_F(CalldataHashingTest, Hash)
7776
auto hash = RawPoseidon2::hash(prepended_calldata_fields);
7877
EXPECT_CALL(poseidon2, hash(prepended_calldata_fields)).WillOnce(Return(hash));
7978

80-
calldata_hasher.compute_calldata_hash(calldata);
79+
calldata_hasher.assert_calldata_hash(hash, calldata);
8180
EXPECT_THAT(
8281
calldata_events.dump_events(),
8382
AllOf(SizeIs(1),
@@ -93,7 +92,7 @@ TEST_F(CalldataHashingTest, Empty)
9392
auto hash = RawPoseidon2::hash(prepended_calldata_fields);
9493
EXPECT_CALL(poseidon2, hash(prepended_calldata_fields)).WillOnce(Return(hash));
9594

96-
calldata_hasher.compute_calldata_hash(calldata);
95+
calldata_hasher.assert_calldata_hash(hash, calldata);
9796
EXPECT_THAT(
9897
calldata_events.dump_events(),
9998
AllOf(SizeIs(1),

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/context_provider.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ std::unique_ptr<ContextInterface> ContextProvider::make_enqueued_context(AztecAd
4545
AztecAddress msg_sender,
4646
FF transaction_fee,
4747
std::span<const FF> calldata,
48+
const FF& calldata_hash,
4849
bool is_static,
4950
Gas gas_limit,
5051
Gas gas_used,
@@ -56,7 +57,7 @@ std::unique_ptr<ContextInterface> ContextProvider::make_enqueued_context(AztecAd
5657
BB_ASSERT_LTE(context_id, std::numeric_limits<uint16_t>::max(), "Context ID out of bounds");
5758
uint16_t space_id = static_cast<uint16_t>(context_id);
5859

59-
cd_hash_provider.make_calldata_hasher(context_id)->compute_calldata_hash(calldata);
60+
cd_hash_provider.make_calldata_hasher(context_id)->assert_calldata_hash(calldata_hash, calldata);
6061

6162
return std::make_unique<EnqueuedCallContext>(
6263
context_id,

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/context_provider.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class ContextProvider : public ContextProviderInterface {
4848
AztecAddress msg_sender,
4949
FF transaction_fee,
5050
std::span<const FF> calldata,
51+
const FF& calldata_hash,
5152
bool is_static,
5253
Gas gas_limit,
5354
Gas gas_used,

barretenberg/cpp/src/barretenberg/vm2/simulation/gadgets/tx_execution.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ TxExecutionResult TxExecution::simulate(const Tx& tx)
107107
call.request.msg_sender,
108108
/*transaction_fee=*/FF(0),
109109
call.calldata,
110+
call.request.calldata_hash,
110111
call.request.is_static_call,
111112
gas_limit,
112113
start_gas,
@@ -166,6 +167,7 @@ TxExecutionResult TxExecution::simulate(const Tx& tx)
166167
call.request.msg_sender,
167168
/*transaction_fee=*/FF(0),
168169
call.calldata,
170+
call.request.calldata_hash,
169171
call.request.is_static_call,
170172
gas_limit,
171173
start_gas,
@@ -229,6 +231,7 @@ TxExecutionResult TxExecution::simulate(const Tx& tx)
229231
teardown_enqueued_call.request.msg_sender,
230232
fee,
231233
teardown_enqueued_call.calldata,
234+
teardown_enqueued_call.request.calldata_hash,
232235
teardown_enqueued_call.request.is_static_call,
233236
teardown_gas_limit,
234237
start_gas,

barretenberg/cpp/src/barretenberg/vm2/simulation/interfaces/calldata_hashing.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace bb::avm2::simulation {
1111
class CalldataHashingInterface {
1212
public:
1313
virtual ~CalldataHashingInterface() = default;
14-
virtual FF compute_calldata_hash(std::span<const FF> calldata) = 0;
14+
virtual void assert_calldata_hash(const FF& cd_hash, std::span<const FF> calldata) = 0;
1515
};
1616

1717
class CalldataHashingProviderInterface {

barretenberg/cpp/src/barretenberg/vm2/simulation/interfaces/context_provider.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ContextProviderInterface {
3030
AztecAddress msg_sender,
3131
FF transaction_fee,
3232
std::span<const FF> calldata,
33+
const FF& calldata_hash,
3334
bool is_static,
3435
Gas gas_limit,
3536
Gas gas_used,

barretenberg/cpp/src/barretenberg/vm2/simulation/testing/mock_cd_hasher.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ class MockCalldataHasher : public CalldataHashingInterface {
1111
MockCalldataHasher();
1212
~MockCalldataHasher() override;
1313

14-
MOCK_METHOD(FF, compute_calldata_hash, (const std::span<const FF> calldata), (override));
14+
MOCK_METHOD(void, assert_calldata_hash, (const FF& cd_hash, std::span<const FF> calldata), (override));
1515
};
1616
} // namespace bb::avm2::simulation

0 commit comments

Comments
 (0)