3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
16using ::testing::ElementsAre;
17using ::testing::Return;
18using ::testing::ReturnRef;
19using ::testing::StrictMock;
21CollectionLimitsConfig limits = {
22 .max_calldata_size_in_fields = 1024,
23 .max_returndata_size_in_fields = 1024,
26TEST(CallStackMetadataCollectorTest, SingleCallEnterAndExit)
28 CallStackMetadataCollector collector(limits);
32 Gas gas_limit{ 1000, 2000 };
34 std::vector<FF> return_data = {
FF(0x5678),
FF(0x9abc) };
42 collector.notify_enter_call(contract_addr, caller_pc, calldata_provider,
false, gas_limit);
45 ReturnDataProvider return_data_provider = [&return_data](uint32_t ) -> std::vector<FF> {
54 collector.notify_exit_call(
true, exit_pc,
std::nullopt, return_data_provider, internal_call_stack_provider);
57 auto metadata = collector.dump_call_stack_metadata();
58 ASSERT_EQ(metadata.size(), 1U);
60 const auto& call_metadata = metadata[0];
61 EXPECT_EQ(call_metadata.timestamp, 0U);
63 EXPECT_EQ(call_metadata.contract_address, contract_addr);
64 EXPECT_EQ(call_metadata.caller_pc, caller_pc);
65 EXPECT_EQ(call_metadata.internal_call_stack_at_exit, std::vector<PC>({ 0, 10, exit_pc }));
66 EXPECT_EQ(call_metadata.calldata, calldata);
67 EXPECT_EQ(call_metadata.output, return_data);
68 EXPECT_EQ(call_metadata.is_static_call,
false);
69 EXPECT_EQ(call_metadata.gas_limit, gas_limit);
70 EXPECT_EQ(call_metadata.reverted,
false);
72 EXPECT_EQ(call_metadata.num_nested_calls, 0U);
73 EXPECT_TRUE(call_metadata.nested.empty());
76TEST(CallStackMetadataCollectorTest, NestedCalls)
78 CallStackMetadataCollector collector(limits);
86 std::vector<FF> calldata1 = {
FF(0xaaaa) };
87 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
89 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
92 std::vector<FF> calldata2 = {
FF(0xbbbb) };
93 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
95 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
98 std::vector<FF> calldata3 = {
FF(0xcccc) };
99 CalldataProvider calldata_provider3 = [&calldata3](uint32_t ) -> std::vector<FF> {
return calldata3; };
101 collector.notify_enter_call(contract3, 300, calldata_provider3,
true, Gas{ 200, 400 });
104 std::vector<FF> return_data3 = {
FF(0x3333) };
105 ReturnDataProvider return_data_provider3 = [&return_data3](uint32_t ) -> std::vector<FF> {
110 collector.notify_exit_call(
true, 399,
std::nullopt, return_data_provider3, internal_call_stack_provider3);
113 std::vector<FF> return_data2 = {
FF(0x2222) };
114 ReturnDataProvider return_data_provider2 = [&return_data2](uint32_t ) -> std::vector<FF> {
119 collector.notify_exit_call(
120 false, 299,
"Nested call reverted", return_data_provider2, internal_call_stack_provider2);
123 std::vector<FF> return_data1 = {
FF(0x1111) };
124 ReturnDataProvider return_data_provider1 = [&return_data1](uint32_t ) -> std::vector<FF> {
129 collector.notify_exit_call(
true, 199,
std::nullopt, return_data_provider1, internal_call_stack_provider1);
132 auto metadata = collector.dump_call_stack_metadata();
133 ASSERT_EQ(metadata.size(), 1U);
135 const auto& outer_call = metadata[0];
136 EXPECT_EQ(outer_call.timestamp, 0U);
137 EXPECT_EQ(outer_call.num_nested_calls, 1U);
138 EXPECT_EQ(outer_call.nested.size(), 1U);
139 EXPECT_EQ(outer_call.output, return_data1);
140 EXPECT_EQ(outer_call.reverted,
false);
142 EXPECT_EQ(outer_call.internal_call_stack_at_exit, std::vector<PC>({ 100, 199 }));
144 const auto& middle_call = outer_call.nested[0];
145 EXPECT_EQ(middle_call.timestamp, 1U);
146 EXPECT_EQ(middle_call.num_nested_calls, 1U);
147 EXPECT_EQ(middle_call.nested.size(), 1U);
148 EXPECT_EQ(middle_call.output, return_data2);
149 EXPECT_EQ(middle_call.reverted,
true);
150 EXPECT_EQ(middle_call.halting_message,
"Nested call reverted");
151 EXPECT_EQ(middle_call.internal_call_stack_at_exit, std::vector<PC>({ 200, 299 }));
153 const auto& inner_call = middle_call.nested[0];
154 EXPECT_EQ(inner_call.timestamp, 2U);
155 EXPECT_EQ(inner_call.num_nested_calls, 0U);
156 EXPECT_EQ(inner_call.nested.size(), 0U);
157 EXPECT_EQ(inner_call.output, return_data3);
158 EXPECT_EQ(inner_call.reverted,
false);
160 EXPECT_EQ(inner_call.is_static_call,
true);
161 EXPECT_EQ(inner_call.internal_call_stack_at_exit, std::vector<PC>({ 300, 399 }));
164TEST(CallStackMetadataCollectorTest, MultipleSiblingCalls)
166 CallStackMetadataCollector collector(limits);
174 std::vector<FF> calldata0 = {
FF(0x0000) };
175 CalldataProvider calldata_provider0 = [&calldata0](uint32_t ) -> std::vector<FF> {
return calldata0; };
176 collector.notify_enter_call(contract0, 0, calldata_provider0,
false, Gas{ 0, 0 });
179 std::vector<FF> calldata1 = {
FF(0x1111) };
180 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
182 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
184 ReturnDataProvider return_data_provider1 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0xaaaa) }; };
187 collector.notify_exit_call(
true, 150,
std::nullopt, return_data_provider1, internal_call_stack_provider1);
190 std::vector<FF> calldata2 = {
FF(0x2222) };
191 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
193 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
195 ReturnDataProvider return_data_provider2 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0xbbbb) }; };
198 collector.notify_exit_call(
true, 250,
std::nullopt, return_data_provider2, internal_call_stack_provider2);
201 ReturnDataProvider return_data_provider0 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0x0000) }; };
203 collector.notify_exit_call(
true, 0,
std::nullopt, return_data_provider0, internal_call_stack_provider0);
206 auto metadata = collector.dump_call_stack_metadata();
207 ASSERT_EQ(metadata.size(), 1U);
209 const auto& outer_call = metadata[0];
210 EXPECT_EQ(outer_call.timestamp, 0U);
211 EXPECT_EQ(outer_call.num_nested_calls, 2U);
213 ASSERT_EQ(outer_call.nested.size(), 2U);
214 EXPECT_EQ(outer_call.nested[0].timestamp, 1U);
215 EXPECT_EQ(outer_call.nested[0].halting_message,
std::nullopt);
216 EXPECT_EQ(outer_call.nested[1].timestamp, 2U);
217 EXPECT_EQ(outer_call.nested[1].halting_message,
std::nullopt);
218 EXPECT_EQ(outer_call.nested[0].contract_address, contract1);
219 EXPECT_EQ(outer_call.nested[1].contract_address, contract2);
222TEST(CallStackMetadataCollectorTest, CalldataSizeLimit)
224 CallStackMetadataCollector collector(limits);
226 std::vector<FF> large_calldata(2000,
FF(0xabcd));
230 CalldataProvider calldata_provider = [&large_calldata](uint32_t max_size) -> std::vector<FF> {
232 if (large_calldata.size() > max_size) {
233 return std::vector<FF>(large_calldata.begin(), large_calldata.begin() + max_size);
235 return large_calldata;
238 collector.notify_enter_call(contract_addr, 100, calldata_provider,
false, Gas{ 1000, 2000 });
240 ReturnDataProvider return_data_provider = [](uint32_t ) -> std::vector<FF> {
return {}; };
243 collector.notify_exit_call(
true, 200,
std::nullopt, return_data_provider, internal_call_stack_provider);
245 auto metadata = collector.dump_call_stack_metadata();
246 ASSERT_EQ(metadata.size(), 1U);
248 EXPECT_EQ(metadata[0].
calldata.size(), 1024U);
252TEST(CallStackMetadataCollectorTest, ReturnDataSizeLimit)
254 CallStackMetadataCollector collector(limits);
262 collector.notify_enter_call(contract_addr, 100, calldata_provider,
false, Gas{ 1000, 2000 });
264 std::vector<FF> large_return_data(2000,
FF(0xef01));
265 ReturnDataProvider return_data_provider = [&large_return_data](uint32_t max_size) -> std::vector<FF> {
267 if (large_return_data.size() > max_size) {
268 return std::vector<FF>(large_return_data.begin(), large_return_data.begin() + max_size);
270 return large_return_data;
274 collector.notify_exit_call(
true, 200,
std::nullopt, return_data_provider, internal_call_stack_provider);
276 auto metadata = collector.dump_call_stack_metadata();
277 ASSERT_EQ(metadata.size(), 1U);
279 EXPECT_EQ(metadata[0].output.size(), 1024U);
283TEST(CallStackMetadataCollectorTest, PhaseTracking)
285 CallStackMetadataCollector collector(limits);
292 std::vector<FF> calldata1 = {
FF(0xaaaa) };
293 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
295 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
296 collector.notify_exit_call(
300 [](uint32_t) -> std::vector<FF> {
return {}; },
301 []() -> std::vector<PC> {
return { 100, 200 }; });
305 std::vector<FF> calldata2 = {
FF(0xbbbb) };
306 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
308 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
309 collector.notify_exit_call(
313 [](uint32_t) -> std::vector<FF> {
return {}; },
314 []() -> std::vector<PC> {
return { 200, 300 }; });
318 std::vector<FF> calldata3 = {
FF(0xcccc) };
319 CalldataProvider calldata_provider3 = [&calldata3](uint32_t ) -> std::vector<FF> {
return calldata3; };
321 collector.notify_enter_call(contract3, 300, calldata_provider3,
false, Gas{ 200, 400 });
322 collector.notify_exit_call(
326 [](uint32_t) -> std::vector<FF> {
return {}; },
327 []() -> std::vector<PC> {
return { 300, 400 }; });
329 auto metadata = collector.dump_call_stack_metadata();
330 ASSERT_EQ(metadata.size(), 3U);
341class MakeProviderTest :
public ::testing::Test {
346TEST_F(MakeProviderTest, MakeCalldataProviderSuccess)
348 uint32_t cd_size = 5;
350 MemoryValue::from<FF>(
FF(0xaaaa)), MemoryValue::from<FF>(
FF(0xbbbb)), MemoryValue::from<FF>(
FF(0xcccc)),
351 MemoryValue::from<FF>(
FF(0xdddd)), MemoryValue::from<FF>(
FF(0xeeee)),
355 EXPECT_CALL(
mock_context, get_parent_cd_size()).WillOnce(Return(cd_size));
361 EXPECT_CALL(
mock_context, get_calldata(0, cd_size)).WillOnce(Return(calldata_values));
363 auto result = provider(1024);
365 EXPECT_THAT(result, ElementsAre(
FF(0xaaaa),
FF(0xbbbb),
FF(0xcccc),
FF(0xdddd),
FF(0xeeee)));
368TEST_F(MakeProviderTest, MakeCalldataProviderRespectsMaxSize)
370 uint32_t cd_size = 2000;
371 uint32_t max_size = 10;
373 for (
size_t i = 0; i < 2000; ++i) {
374 calldata_values[i] = MemoryValue::from<FF>(
FF(i));
378 EXPECT_CALL(
mock_context, get_parent_cd_size()).WillOnce(Return(cd_size));
383 EXPECT_CALL(
mock_context, get_calldata(0, max_size)).WillOnce([&calldata_values, max_size](uint32_t, uint32_t) {
387 auto result = provider(max_size);
389 ASSERT_EQ(result.size(), max_size);
392TEST_F(MakeProviderTest, MakeReturnDataProviderSuccess)
394 uint32_t rd_addr = 200;
395 uint32_t rd_size = 3;
397 MemoryValue::from<FF>(
FF(0x1111)),
398 MemoryValue::from<FF>(
FF(0x2222)),
399 MemoryValue::from<FF>(
FF(0x3333)),
401 auto zero = MemoryValue::from<FF>(
FF(0));
406 StrictMock<MockMemory>
memory;
407 EXPECT_CALL(::testing::Const(
mock_context), get_memory()).WillOnce(ReturnRef(memory));
409 .WillByDefault([&return_data_values, rd_addr, rd_size, &zero](uint32_t i) ->
const MemoryValue& {
411 if (i < rd_addr || i >= rd_addr + rd_size) {
414 const auto offset_into_rd = i - rd_addr;
415 return return_data_values[offset_into_rd];
417 EXPECT_CALL(memory, get).Times(
static_cast<int>(rd_size));
419 auto result = provider(1024);
421 EXPECT_THAT(result, ElementsAre(
FF(0x1111),
FF(0x2222),
FF(0x3333)));
424TEST_F(MakeProviderTest, MakeReturnDataProviderRespectsMaxSize)
426 uint32_t rd_addr = 0;
427 uint32_t rd_size = 2000;
428 uint32_t max_size = 15;
430 for (
size_t i = 0; i < 2000; ++i) {
431 return_data_values[i] = MemoryValue::from<FF>(
FF(i * 2));
437 StrictMock<MockMemory>
memory;
438 EXPECT_CALL(::testing::Const(
mock_context), get_memory()).WillOnce(ReturnRef(memory));
439 ON_CALL(memory, get).WillByDefault([&return_data_values](uint32_t i) ->
const MemoryValue& {
440 return return_data_values[i];
442 EXPECT_CALL(memory, get).Times(
static_cast<int>(max_size));
444 auto result = provider(max_size);
446 ASSERT_EQ(result.size(), max_size);
std::function< std::vector< PC >()> InternalCallStackProvider
std::function< std::vector< FF >(uint32_t max_size)> CalldataProvider
std::function< std::vector< FF >(uint32_t max_size)> ReturnDataProvider
CalldataProvider make_calldata_provider(const ContextInterface &context)
ReturnDataProvider make_return_data_provider(const ContextInterface &context, uint32_t rd_mem_offset_in_child, uint32_t rd_size)
TEST_F(IPATest, ChallengesAreZero)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::vector< MemoryValue > calldata