@@ -486,7 +486,7 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_ephemeral multiple bloc
486486 SilkwormLibrary silkworm_lib{env_path ()};
487487
488488 const int chain_id{1 };
489- const uint64_t batch_size{256 * kMebi };
489+ const uint64_t batch_size{3000 }; // Small batch size to force multiple iterations
490490 const bool write_change_sets{false }; // We CANNOT write changesets here, TestDatabaseContext db already has them
491491 const bool write_receipts{false }; // We CANNOT write receipts here, TestDatabaseContext db already has them
492492 const bool write_call_traces{false }; // For coherence but don't care
@@ -583,7 +583,7 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_perpetual multiple bloc
583583 SilkwormLibrary silkworm_lib{env_path ()};
584584
585585 const int chain_id{1 };
586- const uint64_t batch_size{256 * kMebi };
586+ const uint64_t batch_size{3000 }; // Small batch size to force multiple iterations
587587 const bool write_change_sets{false }; // We CANNOT write changesets here, TestDatabaseContext db already has them
588588 const bool write_receipts{false }; // We CANNOT write receipts here, TestDatabaseContext db already has them
589589 const bool write_call_traces{false }; // For coherence but don't care
@@ -603,7 +603,7 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_perpetual multiple bloc
603603
604604 // Prepare block template (just 1 tx w/ value transfer)
605605 evmc::address from{0x658bdf435d810c91414ec09147daa6db62406379_address}; // funded in genesis
606- evmc::address to{0x8b299e2b7d7f43c0ce3068263545309ff4ffb521_address }; // untouched address
606+ evmc::address to{0x8b299e2b7d7f43c0ce3068263545309ff4ffb500_address }; // untouched address(es)
607607 intx::uint256 value{1 };
608608
609609 Block block{};
@@ -635,6 +635,7 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_perpetual multiple bloc
635635 block.transactions .erase (block.transactions .cbegin ());
636636 block.transactions .pop_back ();
637637 block.transactions [0 ].nonce ++;
638+ block.transactions [0 ].to ->bytes [19 ]++; // change recipient address to force batch size growth
638639 }
639640
640641 // Execute N blocks using an *internal* txn
@@ -646,16 +647,18 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_perpetual multiple bloc
646647
647648 db::ROTxnManaged ro_txn{env};
648649 REQUIRE (db::read_account (ro_txn, to));
649- CHECK (db::read_account (ro_txn, to)->balance == kBlocks * value);
650+ CHECK (db::read_account (ro_txn, to)->balance == value);
650651 ro_txn.abort ();
651652
652653 // Insert N blocks again
654+ block.transactions [0 ].to = to;
653655 for (size_t i{10 + kBlocks }; i < (10 + 2 * kBlocks ); ++i) {
654656 block.header .number = i;
655657 insert_block (env, block);
656658 block.transactions .erase (block.transactions .cbegin ());
657659 block.transactions .pop_back ();
658660 block.transactions [0 ].nonce ++;
661+ block.transactions [0 ].to ->bytes [19 ]++; // change recipient address to force batch size growth
659662 }
660663
661664 // Execute N blocks using an *internal* txn, then commit
@@ -667,7 +670,140 @@ TEST_CASE_METHOD(CApiTest, "CAPI silkworm_execute_blocks_perpetual multiple bloc
667670
668671 ro_txn = db::ROTxnManaged{env};
669672 REQUIRE (db::read_account (ro_txn, to));
670- CHECK (db::read_account (ro_txn, to)->balance == 2 * kBlocks * value);
673+ CHECK (db::read_account (ro_txn, to)->balance == 2 * value);
674+ }
675+
676+ TEST_CASE_METHOD (CApiTest, " CAPI silkworm_execute_blocks_ephemeral multiple blocks: insufficient buffer memory" , " [silkworm][capi]" ) {
677+ // Use Silkworm as a library with silkworm_init/silkworm_fini automated by RAII
678+ SilkwormLibrary silkworm_lib{env_path ()};
679+
680+ const int chain_id{1 };
681+ const uint64_t batch_size{170 }; // Small batch size to force multiple iterations
682+ const bool write_change_sets{false }; // We CANNOT write changesets here, TestDatabaseContext db already has them
683+ const bool write_receipts{false }; // We CANNOT write receipts here, TestDatabaseContext db already has them
684+ const bool write_call_traces{false }; // For coherence but don't care
685+
686+ auto execute_blocks = [&](auto tx, auto start_block, auto end_block) {
687+ return silkworm_lib.execute_blocks (tx,
688+ chain_id,
689+ start_block,
690+ end_block,
691+ batch_size,
692+ write_change_sets,
693+ write_receipts,
694+ write_call_traces);
695+ };
696+
697+ /* TestDatabaseContext db contains a test chain made up of 9 blocks */
698+
699+ // Prepare block template (just 1 tx w/ value transfer)
700+ evmc::address from{0x658bdf435d810c91414ec09147daa6db62406379_address}; // funded in genesis
701+ evmc::address to{0x8b299e2b7d7f43c0ce3068263545309ff4ffb521_address}; // untouched address
702+ intx::uint256 value{1 };
703+
704+ Block block{};
705+ block.header .gas_limit = 5'000'000 ;
706+ block.header .gas_used = 21'000 ;
707+
708+ static constexpr auto kEncoder = [](Bytes& dest, const Receipt& r) { rlp::encode (dest, r); };
709+ std::vector<Receipt> receipts{
710+ {TransactionType::kLegacy , true , block.header .gas_used , {}, {}},
711+ };
712+ block.header .receipts_root = trie::root_hash (receipts, kEncoder );
713+ block.transactions .resize (1 );
714+ block.transactions [0 ].to = to;
715+ block.transactions [0 ].gas_limit = block.header .gas_limit ;
716+ block.transactions [0 ].type = TransactionType::kLegacy ;
717+ block.transactions [0 ].max_priority_fee_per_gas = 0 ;
718+ block.transactions [0 ].max_fee_per_gas = 20 * kGiga ;
719+ block.transactions [0 ].value = value;
720+ block.transactions [0 ].r = 1 ; // dummy
721+ block.transactions [0 ].s = 1 ; // dummy
722+ block.transactions [0 ].set_sender (from);
723+
724+ constexpr size_t kBlocks {130 };
725+
726+ // Insert N blocks
727+ for (size_t i{10 }; i < 10 + kBlocks ; ++i) {
728+ block.header .number = i;
729+ insert_block (env, block);
730+ block.transactions .erase (block.transactions .cbegin ());
731+ block.transactions .pop_back ();
732+ block.transactions [0 ].nonce ++;
733+ }
734+
735+ // Execute N blocks using an *external* txn, then commit
736+ db::RWTxnManaged external_txn0{env};
737+ BlockNum start_block{10 }, end_block{10 + kBlocks - 1 };
738+ const auto result0{execute_blocks (*external_txn0, start_block, end_block)};
739+ CHECK_NOTHROW (external_txn0.commit_and_stop ());
740+ CHECK (result0.execute_block_result == SILKWORM_INTERNAL_ERROR);
741+ }
742+
743+ TEST_CASE_METHOD (CApiTest, " CAPI silkworm_execute_blocks_perpetual multiple blocks: insufficient buffer memory" , " [silkworm][capi]" ) {
744+ // Use Silkworm as a library with silkworm_init/silkworm_fini automated by RAII
745+ SilkwormLibrary silkworm_lib{env_path ()};
746+
747+ const int chain_id{1 };
748+ const uint64_t batch_size{170 }; // Batch size not enough to process a single block
749+ const bool write_change_sets{false }; // We CANNOT write changesets here, TestDatabaseContext db already has them
750+ const bool write_receipts{false }; // We CANNOT write receipts here, TestDatabaseContext db already has them
751+ const bool write_call_traces{false }; // For coherence but don't care
752+
753+ auto execute_blocks = [&](auto start_block, auto end_block) {
754+ return silkworm_lib.execute_blocks_perpetual (env,
755+ chain_id,
756+ start_block,
757+ end_block,
758+ batch_size,
759+ write_change_sets,
760+ write_receipts,
761+ write_call_traces);
762+ };
763+
764+ /* TestDatabaseContext db contains a test chain made up of 9 blocks */
765+
766+ // Prepare block template (just 1 tx w/ value transfer)
767+ evmc::address from{0x658bdf435d810c91414ec09147daa6db62406379_address}; // funded in genesis
768+ evmc::address to{0x8b299e2b7d7f43c0ce3068263545309ff4ffb500_address}; // untouched address(es)
769+ intx::uint256 value{1 };
770+
771+ Block block{};
772+ block.header .gas_limit = 5'000'000 ;
773+ block.header .gas_used = 21'000 ;
774+
775+ static constexpr auto kEncoder = [](Bytes& dest, const Receipt& r) { rlp::encode (dest, r); };
776+ std::vector<Receipt> receipts{
777+ {TransactionType::kLegacy , true , block.header .gas_used , {}, {}},
778+ };
779+ block.header .receipts_root = trie::root_hash (receipts, kEncoder );
780+ block.transactions .resize (1 );
781+ block.transactions [0 ].to = to;
782+ block.transactions [0 ].gas_limit = block.header .gas_limit ;
783+ block.transactions [0 ].type = TransactionType::kLegacy ;
784+ block.transactions [0 ].max_priority_fee_per_gas = 0 ;
785+ block.transactions [0 ].max_fee_per_gas = 20 * kGiga ;
786+ block.transactions [0 ].value = value;
787+ block.transactions [0 ].r = 1 ; // dummy
788+ block.transactions [0 ].s = 1 ; // dummy
789+ block.transactions [0 ].set_sender (from);
790+
791+ constexpr size_t kBlocks {130 };
792+
793+ // Insert N blocks
794+ for (size_t i{10 }; i < 10 + kBlocks ; ++i) {
795+ block.header .number = i;
796+ insert_block (env, block);
797+ block.transactions .erase (block.transactions .cbegin ());
798+ block.transactions .pop_back ();
799+ block.transactions [0 ].nonce ++;
800+ block.transactions [0 ].to ->bytes [19 ]++; // change recipient address to force batch size growth
801+ }
802+
803+ // Execute N blocks using an *internal* txn
804+ BlockNum start_block{10 }, end_block{10 + kBlocks - 1 };
805+ const auto result0{execute_blocks (start_block, end_block)};
806+ CHECK (result0.execute_block_result == SILKWORM_INTERNAL_ERROR);
671807}
672808
673809TEST_CASE_METHOD (CApiTest, " CAPI silkworm_add_snapshot" , " [silkworm][capi]" ) {
0 commit comments