3#include <gmock/gmock.h>
4#include <gtest/gtest.h>
16using ::testing::ElementsAre;
17using ::testing::Return;
18using ::testing::ReturnRef;
19using ::testing::StrictMock;
21TEST(CallStackMetadataCollectorTest, SingleCallEnterAndExit)
23 CallStackMetadataCollector collector;
25 uint32_t caller_pc = 100;
26 uint32_t exit_pc = 200;
27 Gas gas_limit{ 1000, 2000 };
29 std::vector<FF> return_data = {
FF(0x5678),
FF(0x9abc) };
37 collector.notify_enter_call(contract_addr, caller_pc, calldata_provider,
false, gas_limit);
40 ReturnDataProvider return_data_provider = [&return_data](uint32_t ) -> std::vector<FF> {
49 collector.notify_exit_call(
true, exit_pc,
std::nullopt, return_data_provider, internal_call_stack_provider);
52 auto metadata = collector.dump_call_stack_metadata();
53 ASSERT_EQ(metadata.size(), 1U);
55 const auto& call_metadata = metadata[0];
56 EXPECT_EQ(call_metadata.timestamp, 0U);
58 EXPECT_EQ(call_metadata.contract_address, contract_addr);
59 EXPECT_EQ(call_metadata.caller_pc, caller_pc);
60 EXPECT_EQ(call_metadata.internal_call_stack_at_exit, std::vector<PC>({ 0, 10, exit_pc }));
61 EXPECT_EQ(call_metadata.calldata, calldata);
62 EXPECT_EQ(call_metadata.output, return_data);
63 EXPECT_EQ(call_metadata.is_static_call,
false);
64 EXPECT_EQ(call_metadata.gas_limit, gas_limit);
65 EXPECT_EQ(call_metadata.reverted,
false);
67 EXPECT_EQ(call_metadata.num_nested_calls, 0U);
68 EXPECT_TRUE(call_metadata.nested.empty());
71TEST(CallStackMetadataCollectorTest, NestedCalls)
73 CallStackMetadataCollector collector;
81 std::vector<FF> calldata1 = {
FF(0xaaaa) };
82 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
84 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
87 std::vector<FF> calldata2 = {
FF(0xbbbb) };
88 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
90 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
93 std::vector<FF> calldata3 = {
FF(0xcccc) };
94 CalldataProvider calldata_provider3 = [&calldata3](uint32_t ) -> std::vector<FF> {
return calldata3; };
96 collector.notify_enter_call(contract3, 300, calldata_provider3,
true, Gas{ 200, 400 });
99 std::vector<FF> return_data3 = {
FF(0x3333) };
100 ReturnDataProvider return_data_provider3 = [&return_data3](uint32_t ) -> std::vector<FF> {
105 collector.notify_exit_call(
true, 399,
std::nullopt, return_data_provider3, internal_call_stack_provider3);
108 std::vector<FF> return_data2 = {
FF(0x2222) };
109 ReturnDataProvider return_data_provider2 = [&return_data2](uint32_t ) -> std::vector<FF> {
114 collector.notify_exit_call(
115 false, 299,
"Nested call reverted", return_data_provider2, internal_call_stack_provider2);
118 std::vector<FF> return_data1 = {
FF(0x1111) };
119 ReturnDataProvider return_data_provider1 = [&return_data1](uint32_t ) -> std::vector<FF> {
124 collector.notify_exit_call(
true, 199,
std::nullopt, return_data_provider1, internal_call_stack_provider1);
127 auto metadata = collector.dump_call_stack_metadata();
128 ASSERT_EQ(metadata.size(), 1U);
130 const auto& outer_call = metadata[0];
131 EXPECT_EQ(outer_call.timestamp, 0U);
132 EXPECT_EQ(outer_call.num_nested_calls, 1U);
133 EXPECT_EQ(outer_call.nested.size(), 1U);
134 EXPECT_EQ(outer_call.output, return_data1);
135 EXPECT_EQ(outer_call.reverted,
false);
137 EXPECT_EQ(outer_call.internal_call_stack_at_exit, std::vector<PC>({ 100, 199 }));
139 const auto& middle_call = outer_call.nested[0];
140 EXPECT_EQ(middle_call.timestamp, 1U);
141 EXPECT_EQ(middle_call.num_nested_calls, 1U);
142 EXPECT_EQ(middle_call.nested.size(), 1U);
143 EXPECT_EQ(middle_call.output, return_data2);
144 EXPECT_EQ(middle_call.reverted,
true);
145 EXPECT_EQ(middle_call.halting_message,
"Nested call reverted");
146 EXPECT_EQ(middle_call.internal_call_stack_at_exit, std::vector<PC>({ 200, 299 }));
148 const auto& inner_call = middle_call.nested[0];
149 EXPECT_EQ(inner_call.timestamp, 2U);
150 EXPECT_EQ(inner_call.num_nested_calls, 0U);
151 EXPECT_EQ(inner_call.nested.size(), 0U);
152 EXPECT_EQ(inner_call.output, return_data3);
153 EXPECT_EQ(inner_call.reverted,
false);
155 EXPECT_EQ(inner_call.is_static_call,
true);
156 EXPECT_EQ(inner_call.internal_call_stack_at_exit, std::vector<PC>({ 300, 399 }));
159TEST(CallStackMetadataCollectorTest, MultipleSiblingCalls)
161 CallStackMetadataCollector collector;
169 std::vector<FF> calldata0 = {
FF(0x0000) };
170 CalldataProvider calldata_provider0 = [&calldata0](uint32_t ) -> std::vector<FF> {
return calldata0; };
171 collector.notify_enter_call(contract0, 0, calldata_provider0,
false, Gas{ 0, 0 });
174 std::vector<FF> calldata1 = {
FF(0x1111) };
175 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
177 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
179 ReturnDataProvider return_data_provider1 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0xaaaa) }; };
182 collector.notify_exit_call(
true, 150,
std::nullopt, return_data_provider1, internal_call_stack_provider1);
185 std::vector<FF> calldata2 = {
FF(0x2222) };
186 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
188 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
190 ReturnDataProvider return_data_provider2 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0xbbbb) }; };
193 collector.notify_exit_call(
true, 250,
std::nullopt, return_data_provider2, internal_call_stack_provider2);
196 ReturnDataProvider return_data_provider0 = [](uint32_t ) -> std::vector<FF> {
return {
FF(0x0000) }; };
198 collector.notify_exit_call(
true, 0,
std::nullopt, return_data_provider0, internal_call_stack_provider0);
201 auto metadata = collector.dump_call_stack_metadata();
202 ASSERT_EQ(metadata.size(), 1U);
204 const auto& outer_call = metadata[0];
205 EXPECT_EQ(outer_call.timestamp, 0U);
206 EXPECT_EQ(outer_call.num_nested_calls, 2U);
208 ASSERT_EQ(outer_call.nested.size(), 2U);
209 EXPECT_EQ(outer_call.nested[0].timestamp, 1U);
210 EXPECT_EQ(outer_call.nested[0].halting_message,
std::nullopt);
211 EXPECT_EQ(outer_call.nested[1].timestamp, 2U);
212 EXPECT_EQ(outer_call.nested[1].halting_message,
std::nullopt);
213 EXPECT_EQ(outer_call.nested[0].contract_address, contract1);
214 EXPECT_EQ(outer_call.nested[1].contract_address, contract2);
217TEST(CallStackMetadataCollectorTest, CalldataSizeLimit)
219 CallStackMetadataCollector collector;
221 std::vector<FF> large_calldata(2000,
FF(0xabcd));
225 CalldataProvider calldata_provider = [&large_calldata](uint32_t max_size) -> std::vector<FF> {
227 if (large_calldata.size() > max_size) {
228 return std::vector<FF>(large_calldata.begin(), large_calldata.begin() + max_size);
230 return large_calldata;
233 collector.notify_enter_call(contract_addr, 100, calldata_provider,
false, Gas{ 1000, 2000 });
235 ReturnDataProvider return_data_provider = [](uint32_t ) -> std::vector<FF> {
return {}; };
238 collector.notify_exit_call(
true, 200,
std::nullopt, return_data_provider, internal_call_stack_provider);
240 auto metadata = collector.dump_call_stack_metadata();
241 ASSERT_EQ(metadata.size(), 1U);
243 EXPECT_EQ(metadata[0].
calldata.size(), 1024U);
247TEST(CallStackMetadataCollectorTest, ReturnDataSizeLimit)
249 CallStackMetadataCollector collector;
257 collector.notify_enter_call(contract_addr, 100, calldata_provider,
false, Gas{ 1000, 2000 });
259 std::vector<FF> large_return_data(2000,
FF(0xef01));
260 ReturnDataProvider return_data_provider = [&large_return_data](uint32_t max_size) -> std::vector<FF> {
262 if (large_return_data.size() > max_size) {
263 return std::vector<FF>(large_return_data.begin(), large_return_data.begin() + max_size);
265 return large_return_data;
269 collector.notify_exit_call(
true, 200,
std::nullopt, return_data_provider, internal_call_stack_provider);
271 auto metadata = collector.dump_call_stack_metadata();
272 ASSERT_EQ(metadata.size(), 1U);
274 EXPECT_EQ(metadata[0].output.size(), 1024U);
278TEST(CallStackMetadataCollectorTest, PhaseTracking)
280 CallStackMetadataCollector collector;
287 std::vector<FF> calldata1 = {
FF(0xaaaa) };
288 CalldataProvider calldata_provider1 = [&calldata1](uint32_t ) -> std::vector<FF> {
return calldata1; };
290 collector.notify_enter_call(contract1, 100, calldata_provider1,
false, Gas{ 1000, 2000 });
291 collector.notify_exit_call(
295 [](uint32_t) -> std::vector<FF> {
return {}; },
296 []() -> std::vector<PC> {
return { 100, 200 }; });
300 std::vector<FF> calldata2 = {
FF(0xbbbb) };
301 CalldataProvider calldata_provider2 = [&calldata2](uint32_t ) -> std::vector<FF> {
return calldata2; };
303 collector.notify_enter_call(contract2, 200, calldata_provider2,
false, Gas{ 500, 1000 });
304 collector.notify_exit_call(
308 [](uint32_t) -> std::vector<FF> {
return {}; },
309 []() -> std::vector<PC> {
return { 200, 300 }; });
313 std::vector<FF> calldata3 = {
FF(0xcccc) };
314 CalldataProvider calldata_provider3 = [&calldata3](uint32_t ) -> std::vector<FF> {
return calldata3; };
316 collector.notify_enter_call(contract3, 300, calldata_provider3,
false, Gas{ 200, 400 });
317 collector.notify_exit_call(
321 [](uint32_t) -> std::vector<FF> {
return {}; },
322 []() -> std::vector<PC> {
return { 300, 400 }; });
324 auto metadata = collector.dump_call_stack_metadata();
325 ASSERT_EQ(metadata.size(), 3U);
336class MakeProviderTest :
public ::testing::Test {
341TEST_F(MakeProviderTest, MakeCalldataProviderSuccess)
344 uint32_t cd_size = 5;
346 MemoryValue::from<FF>(
FF(0xaaaa)), MemoryValue::from<FF>(
FF(0xbbbb)), MemoryValue::from<FF>(
FF(0xcccc)),
347 MemoryValue::from<FF>(
FF(0xdddd)), MemoryValue::from<FF>(
FF(0xeeee)),
352 EXPECT_CALL(
mock_context, get_parent_cd_size()).WillOnce(Return(cd_size));
359 auto result = provider(1024);
361 EXPECT_THAT(result, ElementsAre(
FF(0xaaaa),
FF(0xbbbb),
FF(0xcccc),
FF(0xdddd),
FF(0xeeee)));
364TEST_F(MakeProviderTest, MakeCalldataProviderRespectsMaxSize)
367 uint32_t cd_size = 2000;
368 uint32_t max_size = 10;
370 for (
size_t i = 0; i < 2000; ++i) {
371 calldata_values[i] = MemoryValue::from<FF>(
FF(i));
376 EXPECT_CALL(
mock_context, get_parent_cd_size()).WillOnce(Return(cd_size));
382 .WillOnce([&calldata_values, max_size](uint32_t, uint32_t) {
386 auto result = provider(max_size);
388 ASSERT_EQ(result.size(), max_size);
391TEST_F(MakeProviderTest, MakeCalldataProviderHandlesException)
395 uint32_t cd_size = 5;
399 EXPECT_CALL(
mock_context, get_parent_cd_size()).WillOnce(Return(cd_size));
405 .WillOnce(::testing::Throw(std::runtime_error(
"Test exception")));
407 EXPECT_CALL(
mock_context, get_address()).Times(::testing::AnyNumber()).WillRepeatedly(ReturnRef(contract_addr));
408 EXPECT_CALL(
mock_context, get_pc()).Times(::testing::AnyNumber()).WillRepeatedly(Return(200));
410 auto result = provider(1024);
413 EXPECT_TRUE(result.empty());
416TEST_F(MakeProviderTest, MakeReturnDataProviderSuccess)
418 uint32_t rd_addr = 200;
419 uint32_t rd_size = 3;
421 MemoryValue::from<FF>(
FF(0x1111)),
422 MemoryValue::from<FF>(
FF(0x2222)),
423 MemoryValue::from<FF>(
FF(0x3333)),
425 auto zero = MemoryValue::from<FF>(
FF(0));
430 StrictMock<MockMemory>
memory;
431 EXPECT_CALL(::testing::Const(
mock_context), get_memory()).WillOnce(ReturnRef(memory));
433 .WillByDefault([&return_data_values, rd_addr, rd_size, &zero](uint32_t i) ->
const MemoryValue& {
435 if (i < rd_addr || i >= rd_addr + rd_size) {
438 const auto offset_into_rd = i - rd_addr;
439 return return_data_values[offset_into_rd];
441 EXPECT_CALL(memory, get).Times(
static_cast<int>(rd_size));
443 auto result = provider(1024);
445 EXPECT_THAT(result, ElementsAre(
FF(0x1111),
FF(0x2222),
FF(0x3333)));
448TEST_F(MakeProviderTest, MakeReturnDataProviderRespectsMaxSize)
450 uint32_t rd_addr = 0;
451 uint32_t rd_size = 2000;
452 uint32_t max_size = 15;
454 for (
size_t i = 0; i < 2000; ++i) {
455 return_data_values[i] = MemoryValue::from<FF>(
FF(i * 2));
461 StrictMock<MockMemory>
memory;
462 EXPECT_CALL(::testing::Const(
mock_context), get_memory()).WillOnce(ReturnRef(memory));
463 ON_CALL(memory, get).WillByDefault([&return_data_values](uint32_t i) ->
const MemoryValue& {
464 return return_data_values[i];
466 EXPECT_CALL(memory, get).Times(
static_cast<int>(max_size));
468 auto result = provider(max_size);
470 ASSERT_EQ(result.size(), max_size);
473TEST_F(MakeProviderTest, MakeReturnDataProviderHandlesException)
475 uint32_t rd_addr = 200;
476 uint32_t rd_size = 3;
482 StrictMock<MockMemory>
memory;
483 EXPECT_CALL(::testing::Const(
mock_context), get_memory())
484 .WillOnce(::testing::Throw(std::runtime_error(
"Test exception")));
487 EXPECT_CALL(
mock_context, get_address()).Times(::testing::AnyNumber()).WillRepeatedly(ReturnRef(contract_addr));
488 EXPECT_CALL(
mock_context, get_pc()).Times(::testing::AnyNumber()).WillRepeatedly(Return(300));
490 auto result = provider(1024);
493 EXPECT_TRUE(result.empty());
std::function< std::vector< PC >()> InternalCallStackProvider
ReturnDataProvider make_return_data_provider(const ContextInterface &context, uint32_t rd_offset, uint32_t rd_size)
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)
TEST_F(IPATest, ChallengesAreZero)
TEST(BoomerangMegaCircuitBuilder, BasicCircuit)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::vector< MemoryValue > calldata