diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index 2e8ccfcfd5..bc2bd4bc48 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -129,8 +129,8 @@ pub unsafe extern "C" fn arbitrator_load_wavm_binary(binary_path: *const c_char) #[no_mangle] #[cfg(feature = "native")] -pub unsafe extern "C" fn arbitrator_new_finished() -> *mut Machine { - Box::into_raw(Box::new(Machine::new_finished())) +pub unsafe extern "C" fn arbitrator_new_finished(gs: GlobalState) -> *mut Machine { + Box::into_raw(Box::new(Machine::new_finished(gs))) } unsafe fn cstr_to_string(c_str: *const c_char) -> String { diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 7cbdeb3cac..0d39d87e77 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -1569,14 +1569,11 @@ impl Machine { // // This allows the Mahine to be set up to model the final state of the // machine at the end of the execution of a block. - // - // Callers should use set_global_state to set the global state of the - // machine to the global state at the end of the block. - pub fn new_finished() -> Machine { + pub fn new_finished(gs: GlobalState) -> Machine { Machine { steps: 0, status: MachineStatus::Finished, - global_state: Default::default(), + global_state: gs, // The machine is in the Finished state, so nothing else really matters. // values_stacks and frame_stacks cannot be empty for proof serialization, // but everything else can just be entirely blank. diff --git a/arbnode/node.go b/arbnode/node.go index 506e1f0ab0..33967409d3 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -703,7 +703,6 @@ func createNodeImpl( if dp != nil { stakerAddr = dp.Sender() } - log.Info("running as validator", "txSender", stakerAddr, "actingAsWallet", wallet.Address(), "strategy", config.Staker.Strategy) } var batchPoster *BatchPoster diff --git a/bold b/bold index 7f3b9eb79a..3d5ae947ce 160000 --- a/bold +++ b/bold @@ -1 +1 @@ -Subproject commit 7f3b9eb79a07f5644b39f2f3a41053cf68071657 +Subproject commit 3d5ae947ce248ad31b06b2799cbfaf2db585d2fd diff --git a/staker/bold/bold_staker.go b/staker/bold/bold_staker.go index 348b822aac..6d4d43d9f6 100644 --- a/staker/bold/bold_staker.go +++ b/staker/bold/bold_staker.go @@ -196,6 +196,12 @@ func (b *BOLDStaker) Initialize(ctx context.Context) error { return err } walletAddressOrZero := b.wallet.AddressOrZero() + var stakerAddr common.Address + if b.wallet.DataPoster() != nil { + stakerAddr = b.wallet.DataPoster().Sender() + } + log.Info("running as validator", "txSender", stakerAddr, "actingAsWallet", walletAddressOrZero, "mode", b.config.Mode) + if b.blockValidator != nil && b.config.StartValidationFromStaked && !b.blockValidator.Started() { rollupUserLogic, err := boldrollup.NewRollupUserLogic(b.rollupAddress, b.client) if err != nil { diff --git a/staker/bold/bold_state_provider.go b/staker/bold/bold_state_provider.go index 76ab6314fb..0459b72053 100644 --- a/staker/bold/bold_state_provider.go +++ b/staker/bold/bold_state_provider.go @@ -72,26 +72,26 @@ func NewBOLDStateProvider( } // ExecutionStateAfterPreviousState Produces the L2 execution state for the next -// assertion. Returns the state at maxInboxCount or blockChallengeLeafHeight +// assertion. Returns the state at maxSeqInboxCount or blockChallengeLeafHeight // after the previous state, whichever is earlier. If previousGlobalState is -// nil, defaults to returning the state at maxInboxCount. +// nil, defaults to returning the state at maxSeqInboxCount. // // TODO: Check the block validator has validated the execution state we are // proposing. func (s *BOLDStateProvider) ExecutionStateAfterPreviousState( ctx context.Context, - maxInboxCount uint64, + maxSeqInboxCount uint64, previousGlobalState protocol.GoGlobalState, ) (*protocol.ExecutionState, error) { - if maxInboxCount == 0 { + if maxSeqInboxCount == 0 { return nil, errors.New("max inbox count cannot be zero") } - batchIndex := maxInboxCount + batchIndex := maxSeqInboxCount maxNumberOfBlocks := uint64(s.blockChallengeLeafHeight) messageCount, err := s.statelessValidator.InboxTracker().GetBatchMessageCount(batchIndex - 1) if err != nil { if strings.Contains(err.Error(), "not found") { - return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxInboxCount) + return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxSeqInboxCount) } return nil, err } @@ -100,7 +100,7 @@ func (s *BOLDStateProvider) ExecutionStateAfterPreviousState( previousMessageCount, err = s.statelessValidator.InboxTracker().GetBatchMessageCount(previousGlobalState.Batch - 1) if err != nil { if strings.Contains(err.Error(), "not found") { - return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxInboxCount) + return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxSeqInboxCount) } return nil, err } @@ -126,7 +126,7 @@ func (s *BOLDStateProvider) ExecutionStateAfterPreviousState( return nil, err } if !stateValidatedAndMessageCountPastThreshold { - return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxInboxCount) + return nil, fmt.Errorf("%w: batch count %d", l2stateprovider.ErrChainCatchingUp, maxSeqInboxCount) } executionState := &protocol.ExecutionState{ @@ -330,10 +330,7 @@ func (s *BOLDStateProvider) CollectMachineHashes( return nil, err } if vs.IsSome() { - m := server_arb.NewFinishedMachine() - if err := m.SetGlobalState(vs.Unwrap()); err != nil { - return nil, err - } + m := server_arb.NewFinishedMachine(vs.Unwrap()) defer m.Destroy() return []common.Hash{m.Hash()}, nil } @@ -508,10 +505,7 @@ func (s *BOLDStateProvider) CollectProof( return nil, err } if vs.IsSome() { - m := server_arb.NewFinishedMachine() - if err := m.SetGlobalState(vs.Unwrap()); err != nil { - return nil, err - } + m := server_arb.NewFinishedMachine(vs.Unwrap()) defer m.Destroy() return m.ProveNextStep(), nil } diff --git a/staker/legacy/staker.go b/staker/legacy/staker.go index 63ae4f97a9..87f891b9ac 100644 --- a/staker/legacy/staker.go +++ b/staker/legacy/staker.go @@ -354,6 +354,21 @@ func (s *Staker) Initialize(ctx context.Context) error { if walletAddressOrZero != (common.Address{}) { s.updateStakerBalanceMetric(ctx) } + var stakerAddr common.Address + if s.L1Validator.wallet.DataPoster() != nil { + stakerAddr = s.L1Validator.wallet.DataPoster().Sender() + } + whiteListed, err := s.isWhitelisted(ctx) + if err != nil { + return fmt.Errorf("error checking if whitelisted: %w", err) + } + log.Info( + "running as validator", + "txSender", stakerAddr, + "actingAsWallet", walletAddressOrZero, + "whitelisted", whiteListed, + "strategy", s.Strategy(), + ) if s.blockValidator != nil && s.config().StartValidationFromStaked { latestStaked, _, err := s.validatorUtils.LatestStaked(&s.baseCallOpts, s.rollupAddress, walletAddressOrZero) if err != nil { diff --git a/staker/multi_protocol/multi_protocol_staker.go b/staker/multi_protocol/multi_protocol_staker.go index a7ba85ac07..8dd5318cab 100644 --- a/staker/multi_protocol/multi_protocol_staker.go +++ b/staker/multi_protocol/multi_protocol_staker.go @@ -18,6 +18,18 @@ import ( "github.com/offchainlabs/nitro/util/stopwaiter" ) +const boldArt = ` + _______ __ _______ +/ \ / | / \ +$$$$$$$ | ______ $$ | $$$$$$$ | +$$ |__$$ | / \ $$ | $$ | $$ | +$$ $$< /$$$$$$ |$$ | $$ | $$ | +$$$$$$$ |$$ | $$ |$$ | $$ | $$ | +$$ |__$$ |$$ \__$$ |$$ |_____ $$ |__$$ | +$$ $$/ $$ $$/ $$ |$$ $$/ +$$$$$$$/ $$$$$$/ $$$$$$$$/ $$$$$$$/ +` + type MultiProtocolStaker struct { stopwaiter.StopWaiter bridge *bridgegen.IBridge @@ -95,7 +107,8 @@ func (m *MultiProtocolStaker) Initialize(ctx context.Context) error { return err } if boldActive { - log.Info("BOLD protocol is active, initializing BOLD staker") + log.Info("BoLD protocol is active, initializing BoLD staker") + log.Info(boldArt) boldStaker, err := m.setupBoldStaker(ctx, rollupAddress) if err != nil { return err @@ -104,7 +117,7 @@ func (m *MultiProtocolStaker) Initialize(ctx context.Context) error { m.oldStaker = nil return m.boldStaker.Initialize(ctx) } - log.Info("BOLD protocol not detected on startup, using old staker until upgrade") + log.Info("BoLD protocol not detected on startup, using old staker until upgrade") return m.oldStaker.Initialize(ctx) } diff --git a/system_tests/bold_challenge_protocol_test.go b/system_tests/bold_challenge_protocol_test.go index fa2000077c..134d1daeb9 100644 --- a/system_tests/bold_challenge_protocol_test.go +++ b/system_tests/bold_challenge_protocol_test.go @@ -97,8 +97,22 @@ func testChallengeProtocolBOLD(t *testing.T, spawnerOpts ...server_arb.SpawnerOp ownerBal := big.NewInt(params.Ether) ownerBal.Mul(ownerBal, big.NewInt(1_000_000)) l2info.GenerateGenesisAccount("Owner", ownerBal) + sconf := setup.RollupStackConfig{ + UseMockBridge: false, + UseMockOneStepProver: false, + MinimumAssertionPeriod: 0, + } - _, l2nodeA, _, _, l1info, _, l1client, l1stack, assertionChain, stakeTokenAddr := createTestNodeOnL1ForBoldProtocol(t, ctx, true, nil, l2chainConfig, nil, l2info) + _, l2nodeA, _, _, l1info, _, l1client, l1stack, assertionChain, stakeTokenAddr := createTestNodeOnL1ForBoldProtocol( + t, + ctx, + true, + nil, + l2chainConfig, + nil, + sconf, + l2info, + ) defer requireClose(t, l1stack) defer l2nodeA.StopAndWait() @@ -109,7 +123,18 @@ func testChallengeProtocolBOLD(t *testing.T, spawnerOpts ...server_arb.SpawnerOp go keepChainMoving(t, ctx, l1info, l1client) l2nodeConfig := arbnode.ConfigDefaultL1Test() - _, l2nodeB, _ := create2ndNodeWithConfigForBoldProtocol(t, ctx, l2nodeA, l1stack, l1info, &l2info.ArbInitData, l2nodeConfig, nil, stakeTokenAddr) + _, l2nodeB, _ := create2ndNodeWithConfigForBoldProtocol( + t, + ctx, + l2nodeA, + l1stack, + l1info, + &l2info.ArbInitData, + l2nodeConfig, + nil, + sconf, + stakeTokenAddr, + ) defer l2nodeB.StopAndWait() genesisA, err := l2nodeA.Execution.ResultAtPos(0) @@ -494,6 +519,7 @@ func createTestNodeOnL1ForBoldProtocol( nodeConfig *arbnode.Config, chainConfig *params.ChainConfig, _ *node.Config, + rollupStackConf setup.RollupStackConfig, l2infoIn info, ) ( l2info info, currentNode *arbnode.Node, l2client *ethclient.Client, l2stack *node.Node, @@ -554,7 +580,7 @@ func createTestNodeOnL1ForBoldProtocol( Require(t, err) l1TransactionOpts.Value = nil - addresses := deployContractsOnly(t, ctx, l1info, l1client, chainConfig.ChainID, stakeToken) + addresses := deployContractsOnly(t, ctx, l1info, l1client, chainConfig.ChainID, rollupStackConf, stakeToken) rollupUser, err := rollupgen.NewRollupUserLogic(addresses.Rollup, l1client) Require(t, err) chalManagerAddr, err := rollupUser.ChallengeManager(&bind.CallOpts{}) @@ -635,6 +661,7 @@ func deployContractsOnly( l1info info, backend *ethclient.Client, chainId *big.Int, + rollupStackConf setup.RollupStackConfig, stakeToken common.Address, ) *chaininfo.RollupAddresses { l1TransactionOpts := l1info.GetDefaultTransactOpts("RollupOwner", ctx) @@ -679,8 +706,7 @@ func deployContractsOnly( &l1TransactionOpts, l1info.GetAddress("Sequencer"), cfg, - false, // do not use mock bridge. - false, // do not use a mock one-step prover + rollupStackConf, ) Require(t, err) @@ -747,6 +773,7 @@ func create2ndNodeWithConfigForBoldProtocol( l2InitData *statetransfer.ArbosInitializationInfo, nodeConfig *arbnode.Config, stackConfig *node.Config, + rollupStackConf setup.RollupStackConfig, stakeTokenAddr common.Address, ) (*ethclient.Client, *arbnode.Node, *solimpl.AssertionChain) { fatalErrChan := make(chan error, 10) @@ -757,7 +784,7 @@ func create2ndNodeWithConfigForBoldProtocol( Fatal(t, "not geth execution node") } chainConfig := firstExec.ArbInterface.BlockChain().Config() - addresses := deployContractsOnly(t, ctx, l1info, l1client, chainConfig.ChainID, stakeTokenAddr) + addresses := deployContractsOnly(t, ctx, l1info, l1client, chainConfig.ChainID, rollupStackConf, stakeTokenAddr) l1info.SetContract("EvilBridge", addresses.Bridge) l1info.SetContract("EvilSequencerInbox", addresses.SequencerInbox) diff --git a/system_tests/bold_state_provider_test.go b/system_tests/bold_state_provider_test.go index 40578221db..17fa436a83 100644 --- a/system_tests/bold_state_provider_test.go +++ b/system_tests/bold_state_provider_test.go @@ -37,6 +37,7 @@ import ( "github.com/offchainlabs/bold/solgen/go/mocksgen" prefixproofs "github.com/offchainlabs/bold/state-commitments/prefix-proofs" mockmanager "github.com/offchainlabs/bold/testing/mocks/state-provider" + "github.com/offchainlabs/bold/testing/setup" ) func TestChallengeProtocolBOLD_Bisections(t *testing.T) { @@ -354,8 +355,22 @@ func setupBoldStateProvider(t *testing.T, ctx context.Context, blockChallengeHei ownerBal := big.NewInt(params.Ether) ownerBal.Mul(ownerBal, big.NewInt(1_000_000)) l2info.GenerateGenesisAccount("Owner", ownerBal) + sconf := setup.RollupStackConfig{ + UseMockBridge: false, + UseMockOneStepProver: false, + MinimumAssertionPeriod: 0, + } - _, l2node, _, _, l1info, _, l1client, l1stack, _, _ := createTestNodeOnL1ForBoldProtocol(t, ctx, false, nil, l2chainConfig, nil, l2info) + _, l2node, _, _, l1info, _, l1client, l1stack, _, _ := createTestNodeOnL1ForBoldProtocol( + t, + ctx, + false, + nil, + l2chainConfig, + nil, + sconf, + l2info, + ) valnode.TestValidationConfig.UseJit = false _, valStack := createTestValidationNode(t, ctx, &valnode.TestValidationConfig) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 1ec0e6226e..346a5feec4 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -1368,8 +1368,11 @@ func deployOnParentChain( &parentChainTransactionOpts, parentChainInfo.GetAddress("Sequencer"), cfg, - false, // do not use mock bridge. - false, // do not use a mock one-step prover + setup.RollupStackConfig{ + UseMockBridge: false, + UseMockOneStepProver: false, + MinimumAssertionPeriod: 0, + }, ) Require(t, err) addresses = &chaininfo.RollupAddresses{ diff --git a/system_tests/overflow_assertions_test.go b/system_tests/overflow_assertions_test.go new file mode 100644 index 0000000000..c1e0ea36d8 --- /dev/null +++ b/system_tests/overflow_assertions_test.go @@ -0,0 +1,320 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see: +// https://github.com/OffchainLabs/nitro/blob/master/LICENSE.md + +//go:build challengetest && !race + +package arbtest + +import ( + "context" + "math/big" + "os" + "strings" + "testing" + "time" + + "github.com/ccoveille/go-safecast" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" + + protocol "github.com/offchainlabs/bold/chain-abstraction" + challengemanager "github.com/offchainlabs/bold/challenge-manager" + modes "github.com/offchainlabs/bold/challenge-manager/types" + l2stateprovider "github.com/offchainlabs/bold/layer2-state-provider" + "github.com/offchainlabs/bold/solgen/go/bridgegen" + "github.com/offchainlabs/bold/solgen/go/mocksgen" + "github.com/offchainlabs/bold/solgen/go/rollupgen" + "github.com/offchainlabs/bold/testing/setup" + "github.com/offchainlabs/nitro/arbos/l2pricing" + "github.com/offchainlabs/nitro/cmd/chaininfo" + "github.com/offchainlabs/nitro/execution/gethexec" + "github.com/offchainlabs/nitro/staker" + "github.com/offchainlabs/nitro/staker/bold" + "github.com/offchainlabs/nitro/util" + "github.com/offchainlabs/nitro/validator/server_arb" + "github.com/offchainlabs/nitro/validator/server_arb/boldmach" + "github.com/offchainlabs/nitro/validator/valnode" +) + +func TestOverflowAssertions(t *testing.T) { + // Get a simulated geth backend running. + // + // Create enough messages in batches to overflow the block level challenge + // height. (height == 32, messages = 45) + // + // Start the challenge manager with a minimumAssertionPeriod of 7 and make + // sure that it posts overflow-assertions right away instead of waiting for + // the 7 blocks to pass. + Require(t, os.RemoveAll("/tmp/good")) + t.Cleanup(func() { + Require(t, os.RemoveAll("/tmp/good")) + }) + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + var transferGas = util.NormalizeL2GasForL1GasInitial(800_000, params.GWei) // include room for aggregator L1 costs + l2chainConfig := chaininfo.ArbitrumDevTestChainConfig() + l2info := NewBlockChainTestInfo( + t, + types.NewArbitrumSigner(types.NewLondonSigner(l2chainConfig.ChainID)), big.NewInt(l2pricing.InitialBaseFeeWei*2), + transferGas, + ) + // This is important to show that overflow assertions don't wait. + minAssertionBlocks := int64(7) + ownerBal := big.NewInt(params.Ether) + ownerBal.Mul(ownerBal, big.NewInt(1_000_000)) + l2info.GenerateGenesisAccount("Owner", ownerBal) + sconf := setup.RollupStackConfig{ + UseMockBridge: false, + UseMockOneStepProver: false, + MinimumAssertionPeriod: minAssertionBlocks, + } + + _, l2node, _, _, l1info, _, l1client, l1stack, assertionChain, _ := createTestNodeOnL1ForBoldProtocol(t, ctx, true, nil, l2chainConfig, nil, sconf, l2info) + defer requireClose(t, l1stack) + defer l2node.StopAndWait() + + // Make sure we shut down test functionality before the rest of the node + ctx, cancelCtx = context.WithCancel(ctx) + defer cancelCtx() + + go keepChainMoving(t, ctx, l1info, l1client) + + balance := big.NewInt(params.Ether) + balance.Mul(balance, big.NewInt(100)) + TransferBalance(t, "Faucet", "Asserter", balance, l1info, l1client, ctx) + + valCfg := valnode.TestValidationConfig + valCfg.UseJit = false + boldWrapperOpt := server_arb.WithWrapper( + func(inner server_arb.MachineInterface) server_arb.MachineInterface { + return boldmach.MachineWrapper(inner) + }) + _, valStack := createTestValidationNode(t, ctx, &valCfg, boldWrapperOpt) + blockValidatorConfig := staker.TestBlockValidatorConfig + + stateless, err := staker.NewStatelessBlockValidator( + l2node.InboxReader, + l2node.InboxTracker, + l2node.TxStreamer, + l2node.Execution, + l2node.ArbDB, + nil, + StaticFetcherFrom(t, &blockValidatorConfig), + valStack, + ) + Require(t, err) + err = stateless.Start(ctx) + Require(t, err) + + blockValidator, err := staker.NewBlockValidator( + stateless, + l2node.InboxTracker, + l2node.TxStreamer, + StaticFetcherFrom(t, &blockValidatorConfig), + nil, + ) + Require(t, err) + Require(t, blockValidator.Initialize(ctx)) + Require(t, blockValidator.Start(ctx)) + + stateManager, err := bold.NewBOLDStateProvider( + blockValidator, + stateless, + l2stateprovider.Height(blockChallengeLeafHeight), + &bold.StateProviderConfig{ + ValidatorName: "good", + MachineLeavesCachePath: "/tmp/good", + CheckBatchFinality: false, + }, + ) + Require(t, err) + + Require(t, l2node.Start(ctx)) + + l2info.GenerateAccount("Destination") + sequencerTxOpts := l1info.GetDefaultTransactOpts("Sequencer", ctx) + + honestSeqInbox := l1info.GetAddress("SequencerInbox") + honestSeqInboxBinding, err := bridgegen.NewSequencerInbox(honestSeqInbox, l1client) + Require(t, err) + + // Post batches to the honest and inbox. + seqInboxABI, err := abi.JSON(strings.NewReader(bridgegen.SequencerInboxABI)) + Require(t, err) + + honestUpgradeExec, err := mocksgen.NewUpgradeExecutorMock(l1info.GetAddress("UpgradeExecutor"), l1client) + Require(t, err) + data, err := seqInboxABI.Pack( + "setIsBatchPoster", + sequencerTxOpts.From, + true, + ) + Require(t, err) + honestRollupOwnerOpts := l1info.GetDefaultTransactOpts("RollupOwner", ctx) + _, err = honestUpgradeExec.ExecuteCall(&honestRollupOwnerOpts, honestSeqInbox, data) + Require(t, err) + + // Post enough messages (45 across 2 batches) to overflow the block level + // challenge height (32). + totalMessagesPosted := int64(0) + numMessagesPerBatch := int64(32) + divergeAt := int64(-1) + makeBoldBatch(t, l2node, l2info, l1client, &sequencerTxOpts, honestSeqInboxBinding, honestSeqInbox, numMessagesPerBatch, divergeAt) + totalMessagesPosted += numMessagesPerBatch + + numMessagesPerBatch = int64(13) + makeBoldBatch(t, l2node, l2info, l1client, &sequencerTxOpts, honestSeqInboxBinding, honestSeqInbox, numMessagesPerBatch, divergeAt) + totalMessagesPosted += numMessagesPerBatch + + bc, err := l2node.InboxTracker.GetBatchCount() + Require(t, err) + msgs, err := l2node.InboxTracker.GetBatchMessageCount(bc - 1) + Require(t, err) + + t.Logf("Node batch count %d, msgs %d", bc, msgs) + + // Wait for the node to catch up. + nodeExec, ok := l2node.Execution.(*gethexec.ExecutionNode) + if !ok { + Fatal(t, "not geth execution node") + } + for { + latest := nodeExec.Backend.APIBackend().CurrentHeader() + isCaughtUp := latest.Number.Uint64() == uint64(totalMessagesPosted) + if isCaughtUp { + break + } + time.Sleep(time.Millisecond * 200) + } + + bridgeBinding, err := bridgegen.NewBridge(l1info.GetAddress("Bridge"), l1client) + Require(t, err) + totalBatchesBig, err := bridgeBinding.SequencerMessageCount(&bind.CallOpts{Context: ctx}) + Require(t, err) + totalBatches := totalBatchesBig.Uint64() + + // Wait until the validator has validated the batches. + for { + lastInfo, err := blockValidator.ReadLastValidatedInfo() + if lastInfo == nil || err != nil { + continue + } + t.Log("Batch", lastInfo.GlobalState.Batch, "Total", totalBatches-1) + if lastInfo.GlobalState.Batch >= totalBatches-1 { + break + } + time.Sleep(time.Millisecond * 200) + } + + provider := l2stateprovider.NewHistoryCommitmentProvider( + stateManager, + stateManager, + stateManager, + []l2stateprovider.Height{ + l2stateprovider.Height(blockChallengeLeafHeight), + l2stateprovider.Height(bigStepChallengeLeafHeight), + l2stateprovider.Height(smallStepChallengeLeafHeight), + }, + stateManager, + nil, // Api db + ) + + stackOpts := []challengemanager.StackOpt{ + challengemanager.StackWithName("default"), + challengemanager.StackWithMode(modes.MakeMode), + challengemanager.StackWithPostingInterval(time.Second), + challengemanager.StackWithPollingInterval(time.Millisecond * 500), + challengemanager.StackWithAverageBlockCreationTime(time.Second), + } + + manager, err := challengemanager.NewChallengeStack( + assertionChain, + provider, + stackOpts..., + ) + Require(t, err) + manager.Start(ctx) + + filterer, err := rollupgen.NewRollupUserLogicFilterer(assertionChain.RollupAddress(), assertionChain.Backend()) + Require(t, err) + + // The goal of this test is to observe: + // + // 1. The genisis assertion (non-overflow) + // 2. The assertion of the first 32 blocks of the two batches manually set up + // above (non-overflow) + // 3. The overflow assertion that should be posted in fewer than + // minAssertionBlocks. (overflow) + // 4. One more normal assertion in >= minAssertionBlocks. (non-overflow) + + overflow := true + nonOverflow := false + expectedAssertions := []bool{nonOverflow, nonOverflow, overflow, nonOverflow} + mab64, err := safecast.ToUint64(minAssertionBlocks) + Require(t, err) + + lastInboxMax := uint64(0) + lastAssertionBlock := uint64(0) + fromBlock := uint64(0) + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for len(expectedAssertions) > 0 { + select { + case <-ticker.C: + latestBlock, err := l1client.HeaderByNumber(ctx, nil) + Require(t, err) + toBlock := latestBlock.Number.Uint64() + if fromBlock >= toBlock { + continue + } + filterOpts := &bind.FilterOpts{ + Start: fromBlock, + End: &toBlock, + Context: ctx, + } + it, err := filterer.FilterAssertionCreated(filterOpts, nil, nil) + Require(t, err) + for it.Next() { + if it.Error() != nil { + t.Fatalf("Error in filter iterator: %v", it.Error()) + } + t.Log("Received event of assertion created!") + assertionHash := protocol.AssertionHash{Hash: it.Event.AssertionHash} + creationInfo, err := assertionChain.ReadAssertionCreationInfo(ctx, assertionHash) + Require(t, err) + t.Logf("Created assertion in block: %d", creationInfo.CreationBlock) + newState := protocol.GoGlobalStateFromSolidity(creationInfo.AfterState.GlobalState) + t.Logf("NewState PosInBatch: %d", newState.PosInBatch) + inboxMax := creationInfo.InboxMaxCount.Uint64() + t.Logf("InboxMax: %d", inboxMax) + blocks := creationInfo.CreationBlock - lastAssertionBlock + // PosInBatch == 0 && inboxMax > lastInboxMax means it is NOT an overflow assertion. + if newState.PosInBatch == 0 && inboxMax > lastInboxMax { + if expectedAssertions[0] == overflow { + t.Errorf("Expected overflow assertion, got non-overflow assertion") + } + if blocks < mab64 { + t.Errorf("non-overflow assertions should have >= =%d blocks between them. Got: %d", mab64, blocks) + } + } else { + if expectedAssertions[0] == nonOverflow { + t.Errorf("Expected non-overflow assertion, got overflow assertion") + } + if blocks >= mab64 { + t.Errorf("overflow assertions should not have %d blocks between them. Got: %d", mab64, blocks) + } + } + lastAssertionBlock = creationInfo.CreationBlock + lastInboxMax = inboxMax + expectedAssertions = expectedAssertions[1:] + } + fromBlock = toBlock + 1 + case <-ctx.Done(): + return + } + } + // PASS: All expected assertions were seen. +} diff --git a/validator/server_arb/boldmach/machine.go b/validator/server_arb/boldmach/machine.go index ba51fe6b72..914c7b21d7 100644 --- a/validator/server_arb/boldmach/machine.go +++ b/validator/server_arb/boldmach/machine.go @@ -20,12 +20,7 @@ type boldMachine struct { var _ server_arb.MachineInterface = (*boldMachine)(nil) func newBoldMachine(inner server_arb.MachineInterface) *boldMachine { - z := server_arb.NewFinishedMachine() - if err := z.SetGlobalState(inner.GetGlobalState()); err != nil { - // This should only occur if the machine is frozen, - // which it isn't because we just created it. - panic(err) - } + z := server_arb.NewFinishedMachine(inner.GetGlobalState()) return &boldMachine{ inner: inner, zeroMachine: z, diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index 9bd3904301..09a00635fb 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -118,8 +118,8 @@ func LoadSimpleMachine(wasm string, libraries []string, debugChain bool) (*Arbit return machineFromPointer(mach), nil } -func NewFinishedMachine() *ArbitratorMachine { - mach := C.arbitrator_new_finished() +func NewFinishedMachine(gs validator.GoGlobalState) *ArbitratorMachine { + mach := C.arbitrator_new_finished(GlobalStateToC(gs)) if mach == nil { return nil }