From ade061383bc592de4b89b41eacdec2ccea6ee13d Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 1 Feb 2022 10:36:39 -0600 Subject: [PATCH 001/110] WIP: Add staker code --- arbnode/batch_poster.go | 5 +- arbnode/delayed.go | 5 +- arbnode/delayed_sequencer.go | 5 +- arbnode/inbox_reader.go | 5 +- arbnode/node.go | 17 +- arbnode/sequencer.go | 5 +- arbnode/util.go => arbutil/wait_for_l1.go | 6 +- system_tests/common_test.go | 9 +- system_tests/delayedinbox_test.go | 6 +- system_tests/delayedinboxlong_test.go | 6 +- system_tests/estimation_test.go | 12 +- system_tests/full_challenge_test.go | 14 +- system_tests/retryable_test.go | 20 +- system_tests/seqfeed_test.go | 13 +- system_tests/seqinbox_test.go | 10 +- system_tests/transfer_test.go | 4 +- system_tests/twonodes_test.go | 6 +- system_tests/twonodeslong_test.go | 10 +- system_tests/validator_test.go | 6 +- validator/l1_validator.go | 475 ++++++++++++++++++++++ validator/staker.go | 475 ++++++++++++++++++++++ 21 files changed, 1038 insertions(+), 76 deletions(-) rename arbnode/util.go => arbutil/wait_for_l1.go (98%) create mode 100644 validator/l1_validator.go create mode 100644 validator/staker.go diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index e1691d7db1..2f9166a2af 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -15,11 +15,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" ) type BatchPoster struct { - client L1Interface + client arbutil.L1Interface inbox *InboxTracker streamer *TransactionStreamer config *BatchPosterConfig @@ -50,7 +51,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ CompressionLevel: 2, } -func NewBatchPoster(client L1Interface, inbox *InboxTracker, streamer *TransactionStreamer, config *BatchPosterConfig, contractAddress common.Address, refunder common.Address, transactOpts *bind.TransactOpts) (*BatchPoster, error) { +func NewBatchPoster(client arbutil.L1Interface, inbox *InboxTracker, streamer *TransactionStreamer, config *BatchPosterConfig, contractAddress common.Address, refunder common.Address, transactOpts *bind.TransactOpts) (*BatchPoster, error) { inboxContract, err := bridgegen.NewSequencerInbox(contractAddress, client) if err != nil { return nil, err diff --git a/arbnode/delayed.go b/arbnode/delayed.go index 4b18aba30a..5f0e2d5acb 100644 --- a/arbnode/delayed.go +++ b/arbnode/delayed.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" ) @@ -54,11 +55,11 @@ type DelayedBridge struct { con *bridgegen.IBridge address common.Address fromBlock uint64 - client L1Interface + client arbutil.L1Interface messageProviders map[common.Address]*bridgegen.IMessageProvider } -func NewDelayedBridge(client L1Interface, addr common.Address, fromBlock uint64) (*DelayedBridge, error) { +func NewDelayedBridge(client arbutil.L1Interface, addr common.Address, fromBlock uint64) (*DelayedBridge, error) { con, err := bridgegen.NewIBridge(addr, client) if err != nil { return nil, err diff --git a/arbnode/delayed_sequencer.go b/arbnode/delayed_sequencer.go index 8a520580e4..c6770f7c88 100644 --- a/arbnode/delayed_sequencer.go +++ b/arbnode/delayed_sequencer.go @@ -10,10 +10,11 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" ) type DelayedSequencer struct { - client L1Interface + client arbutil.L1Interface bridge *DelayedBridge inbox *InboxTracker txStreamer *TransactionStreamer @@ -39,7 +40,7 @@ var TestDelayedSequencerConfig = DelayedSequencerConfig{ TimeAggregate: time.Second, } -func NewDelayedSequencer(client L1Interface, reader *InboxReader, txStreamer *TransactionStreamer, config *DelayedSequencerConfig) (*DelayedSequencer, error) { +func NewDelayedSequencer(client arbutil.L1Interface, reader *InboxReader, txStreamer *TransactionStreamer, config *DelayedSequencerConfig) (*DelayedSequencer, error) { return &DelayedSequencer{ client: client, bridge: reader.DelayedBridge(), diff --git a/arbnode/inbox_reader.go b/arbnode/inbox_reader.go index d5d8be8924..cbdac41efd 100644 --- a/arbnode/inbox_reader.go +++ b/arbnode/inbox_reader.go @@ -11,6 +11,7 @@ import ( "time" "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/arbstate/arbutil" ) type InboxReaderConfig struct { @@ -42,10 +43,10 @@ type InboxReader struct { delayedBridge *DelayedBridge sequencerInbox *SequencerInbox caughtUpChan chan bool - client L1Interface + client arbutil.L1Interface } -func NewInboxReader(tracker *InboxTracker, client L1Interface, firstMessageBlock *big.Int, delayedBridge *DelayedBridge, sequencerInbox *SequencerInbox, config *InboxReaderConfig) (*InboxReader, error) { +func NewInboxReader(tracker *InboxTracker, client arbutil.L1Interface, firstMessageBlock *big.Int, delayedBridge *DelayedBridge, sequencerInbox *SequencerInbox, config *InboxReaderConfig) (*InboxReader, error) { return &InboxReader{ tracker: tracker, delayedBridge: delayedBridge, diff --git a/arbnode/node.go b/arbnode/node.go index 900aecde75..a143d1677f 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -26,6 +26,7 @@ import ( "github.com/offchainlabs/arbstate/arbos/arbosState" "github.com/offchainlabs/arbstate/arbos/l2pricing" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/broadcastclient" "github.com/offchainlabs/arbstate/broadcaster" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" @@ -43,18 +44,18 @@ type RollupAddresses struct { DeployedAt uint64 } -func andTxSucceeded(ctx context.Context, l1client L1Interface, txTimeout time.Duration, tx *types.Transaction, err error) error { +func andTxSucceeded(ctx context.Context, l1client arbutil.L1Interface, txTimeout time.Duration, tx *types.Transaction, err error) error { if err != nil { return fmt.Errorf("error submitting tx: %w", err) } - _, err = EnsureTxSucceededWithTimeout(ctx, l1client, tx, txTimeout) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l1client, tx, txTimeout) if err != nil { return fmt.Errorf("error executing tx: %w", err) } return nil } -func deployBridgeCreator(ctx context.Context, client L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (common.Address, error) { +func deployBridgeCreator(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (common.Address, error) { bridgeTemplate, tx, _, err := bridgegen.DeployBridge(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { @@ -100,7 +101,7 @@ func deployBridgeCreator(ctx context.Context, client L1Interface, auth *bind.Tra return bridgeCreatorAddr, nil } -func deployChallengeFactory(ctx context.Context, client L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (common.Address, error) { +func deployChallengeFactory(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (common.Address, error) { osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { @@ -134,7 +135,7 @@ func deployChallengeFactory(ctx context.Context, client L1Interface, auth *bind. return ospEntryAddr, nil } -func deployRollupCreator(ctx context.Context, client L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (*rollupgen.RollupCreator, error) { +func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (*rollupgen.RollupCreator, error) { bridgeCreator, err := deployBridgeCreator(ctx, client, auth, txTimeout) if err != nil { return nil, err @@ -178,7 +179,7 @@ func deployRollupCreator(ctx context.Context, client L1Interface, auth *bind.Tra return rollupCreator, nil } -func DeployOnL1(ctx context.Context, l1client L1Interface, deployAuth *bind.TransactOpts, sequencer common.Address, wasmModuleRoot common.Hash, txTimeout time.Duration) (*RollupAddresses, error) { +func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *bind.TransactOpts, sequencer common.Address, wasmModuleRoot common.Hash, txTimeout time.Duration) (*RollupAddresses, error) { rollupCreator, err := deployRollupCreator(ctx, l1client, deployAuth, txTimeout) if err != nil { return nil, err @@ -206,7 +207,7 @@ func DeployOnL1(ctx context.Context, l1client L1Interface, deployAuth *bind.Tran if err != nil { return nil, fmt.Errorf("error submitting create rollup tx: %w", err) } - receipt, err := EnsureTxSucceededWithTimeout(ctx, l1client, tx, txTimeout) + receipt, err := arbutil.EnsureTxSucceededWithTimeout(ctx, l1client, tx, txTimeout) if err != nil { return nil, fmt.Errorf("error executing create rollup tx: %w", err) } @@ -268,7 +269,7 @@ type Node struct { BroadcastClient *broadcastclient.BroadcastClient } -func CreateNode(stack *node.Node, chainDb ethdb.Database, config *NodeConfig, l2BlockChain *core.BlockChain, l1client L1Interface, deployInfo *RollupAddresses, sequencerTxOpt *bind.TransactOpts) (*Node, error) { +func CreateNode(stack *node.Node, chainDb ethdb.Database, config *NodeConfig, l2BlockChain *core.BlockChain, l1client arbutil.L1Interface, deployInfo *RollupAddresses, sequencerTxOpt *bind.TransactOpts) (*Node, error) { var broadcastServer *broadcaster.Broadcaster if config.Broadcaster { broadcastServer = broadcaster.NewBroadcaster(config.BroadcasterConfig) diff --git a/arbnode/sequencer.go b/arbnode/sequencer.go index 146048abd4..1bdb9921db 100644 --- a/arbnode/sequencer.go +++ b/arbnode/sequencer.go @@ -14,16 +14,17 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbos/l1pricing" + "github.com/offchainlabs/arbstate/arbutil" "github.com/pkg/errors" ) type Sequencer struct { txStreamer *TransactionStreamer - l1Client L1Interface + l1Client arbutil.L1Interface l1BlockNumber uint64 } -func NewSequencer(txStreamer *TransactionStreamer, l1Client L1Interface) (*Sequencer, error) { +func NewSequencer(txStreamer *TransactionStreamer, l1Client arbutil.L1Interface) (*Sequencer, error) { return &Sequencer{ txStreamer: txStreamer, l1Client: l1Client, diff --git a/arbnode/util.go b/arbutil/wait_for_l1.go similarity index 98% rename from arbnode/util.go rename to arbutil/wait_for_l1.go index 8baf1a77c9..d65b4ca91d 100644 --- a/arbnode/util.go +++ b/arbutil/wait_for_l1.go @@ -1,4 +1,8 @@ -package arbnode +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package arbutil import ( "context" diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 31d8f860af..a54638f907 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/statetransfer" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -31,7 +32,7 @@ import ( ) type info = *BlockchainTestInfo -type client = arbnode.L1Interface +type client = arbutil.L1Interface func SendWaitTestTransactions(t *testing.T, ctx context.Context, client client, txs []*types.Transaction) { t.Helper() @@ -39,7 +40,7 @@ func SendWaitTestTransactions(t *testing.T, ctx context.Context, client client, Require(t, client.SendTransaction(ctx, tx)) } for _, tx := range txs { - _, err := arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err := arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) } } @@ -48,7 +49,7 @@ func TransferBalance(t *testing.T, from, to string, amount *big.Int, l2info info tx := l2info.PrepareTx(from, to, l2info.TransferGas, amount, nil) err := client.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) } @@ -203,7 +204,7 @@ func CreateTestL2WithConfig(t *testing.T, ctx context.Context, l2Info *Blockchai tx, err := arbdebug.BecomeChainOwner(&debugAuth) Require(t, err, "failed to deploy ArbDebug") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) } diff --git a/system_tests/delayedinbox_test.go b/system_tests/delayedinbox_test.go index 70268ec249..858f70adca 100644 --- a/system_tests/delayedinbox_test.go +++ b/system_tests/delayedinbox_test.go @@ -13,8 +13,8 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" ) @@ -55,7 +55,7 @@ func TestDelayInboxSimple(t *testing.T) { txwrapped := append([]byte{arbos.L2MessageKind_SignedTx}, txbytes...) l1tx, err := delayedInboxContract.SendL2Message(&usertxopts, txwrapped) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, l1client, l1tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in @@ -64,7 +64,7 @@ func TestDelayInboxSimple(t *testing.T) { l1info.PrepareTx("Faucet", "User", 30000, big.NewInt(1e12), nil), }) } - _, err = arbnode.WaitForTx(ctx, l2client, delayedTx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2client, delayedTx.Hash(), time.Second*5) Require(t, err) l2balance, err := l2client.BalanceAt(ctx, l2info.GetAddress("User2"), nil) Require(t, err) diff --git a/system_tests/delayedinboxlong_test.go b/system_tests/delayedinboxlong_test.go index e97c4fe1be..5558d2c0ce 100644 --- a/system_tests/delayedinboxlong_test.go +++ b/system_tests/delayedinboxlong_test.go @@ -16,7 +16,7 @@ import ( "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" ) func TestDelayInboxLong(t *testing.T) { @@ -57,7 +57,7 @@ func TestDelayInboxLong(t *testing.T) { Require(t, err) } // Checking every tx is expensive, so we just check the last, assuming that the others succeeded too - _, err := arbnode.EnsureTxSucceeded(ctx, l1client, l1Txs[len(l1Txs)-1]) + _, err := arbutil.EnsureTxSucceeded(ctx, l1client, l1Txs[len(l1Txs)-1]) Require(t, err) } @@ -73,7 +73,7 @@ func TestDelayInboxLong(t *testing.T) { }) } - _, err := arbnode.WaitForTx(ctx, l2client, lastDelayedMessage.Hash(), time.Second*5) + _, err := arbutil.WaitForTx(ctx, l2client, lastDelayedMessage.Hash(), time.Second*5) Require(t, err) l2balance, err := l2client.BalanceAt(ctx, l2info.GetAddress("User2"), nil) Require(t, err) diff --git a/system_tests/estimation_test.go b/system_tests/estimation_test.go index 7f7cf08c67..b142962da0 100644 --- a/system_tests/estimation_test.go +++ b/system_tests/estimation_test.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/mocksgen" "github.com/offchainlabs/arbstate/solgen/go/precompilesgen" ) @@ -27,12 +27,12 @@ func TestDeploy(t *testing.T) { _, tx, simple, err := mocksgen.DeploySimple(&auth, client) Require(t, err, "could not deploy contract") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) tx, err = simple.Increment(&auth) Require(t, err, "failed to call Increment()") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) counter, err := simple.Counter(&bind.CallOpts{}) @@ -58,7 +58,7 @@ func TestEstimate(t *testing.T) { Require(t, err, "could not deploy ArbOwner contract") tx, err := arbOwner.SetMinimumGasPrice(&auth, gasPrice) Require(t, err, "could not set L2 gas price") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) // make an empty block to let the gas price update @@ -79,7 +79,7 @@ func TestEstimate(t *testing.T) { // deploy a test contract _, tx, simple, err := mocksgen.DeploySimple(&auth, client) Require(t, err, "could not deploy contract") - receipt, err := arbnode.EnsureTxSucceeded(ctx, client, tx) + receipt, err := arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) header, err := client.HeaderByNumber(ctx, receipt.BlockNumber) @@ -98,7 +98,7 @@ func TestEstimate(t *testing.T) { tx, err = simple.Increment(&auth) Require(t, err, "failed to call Increment()") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) counter, err := simple.Counter(&bind.CallOpts{}) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index a6ab3da89f..ada6495726 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -54,7 +54,7 @@ func DeployOneStepProofEntry(t *testing.T, auth *bind.TransactOpts, client *ethc if err != nil { t.Fatal(err) } - _, err = arbnode.EnsureTxSucceeded(context.Background(), client, tx) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } @@ -84,13 +84,13 @@ func CreateChallenge( if err != nil { t.Fatal(err) } - _, err = arbnode.EnsureTxSucceeded(context.Background(), client, tx) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } challengeAddr, tx, challenge, err := challengegen.DeployBlockChallenge(auth, client) - _, err = arbnode.EnsureTxSucceeded(context.Background(), client, tx) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } @@ -121,7 +121,7 @@ func CreateChallenge( if err != nil { t.Fatal(err) } - _, err = arbnode.EnsureTxSucceeded(context.Background(), client, tx) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } @@ -166,7 +166,7 @@ func makeBatch(t *testing.T, l2Node *arbnode.Node, l2Info *BlockchainTestInfo, b if err != nil { t.Fatal(err) } - receipt, err := arbnode.EnsureTxSucceeded(ctx, backend, tx) + receipt, err := arbutil.EnsureTxSucceeded(ctx, backend, tx) if err != nil { t.Fatal(err) } @@ -192,7 +192,7 @@ func makeBatch(t *testing.T, l2Node *arbnode.Node, l2Info *BlockchainTestInfo, b } } -func confirmLatestBlock(ctx context.Context, t *testing.T, l1Info *BlockchainTestInfo, backend arbnode.L1Interface) { +func confirmLatestBlock(ctx context.Context, t *testing.T, l1Info *BlockchainTestInfo, backend arbutil.L1Interface) { for i := 0; i < 12; i++ { SendWaitTestTransactions(t, ctx, backend, []*types.Transaction{ l1Info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), @@ -362,7 +362,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { if tx == nil { t.Fatal("no move") } - _, err = arbnode.EnsureTxSucceeded(ctx, backend, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, backend, tx) if err != nil { if !currentCorrect && strings.Contains(err.Error(), "BAD_SEQINBOX_MESSAGE") { t.Log("challenge complete! Tx failed as expected:", err) diff --git a/system_tests/retryable_test.go b/system_tests/retryable_test.go index b581e79685..b6a87196a5 100644 --- a/system_tests/retryable_test.go +++ b/system_tests/retryable_test.go @@ -14,8 +14,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" "github.com/offchainlabs/arbstate/solgen/go/precompilesgen" "github.com/offchainlabs/arbstate/util" @@ -77,7 +77,7 @@ func TestSubmitRetryableImmediateSuccess(t *testing.T) { ) Require(t, err) - l1receipt, err := arbnode.EnsureTxSucceeded(ctx, l1client, l1tx) + l1receipt, err := arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) if l1receipt.Status != types.ReceiptStatusSuccessful { Fail(t, "l1receipt indicated failure") @@ -97,7 +97,7 @@ func TestSubmitRetryableImmediateSuccess(t *testing.T) { waitForL1DelayBlocks(t, ctx, l1client, l1info) - receipt, err := arbnode.WaitForTx(ctx, l2client, *l2TxId, time.Second*5) + receipt, err := arbutil.WaitForTx(ctx, l2client, *l2TxId, time.Second*5) Require(t, err) if receipt.Status != types.ReceiptStatusSuccessful { Fail(t) @@ -134,7 +134,7 @@ func TestSubmitRetryableFailThenRetry(t *testing.T) { ) Require(t, err) - l1receipt, err := arbnode.EnsureTxSucceeded(ctx, l1client, l1tx) + l1receipt, err := arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) if l1receipt.Status != types.ReceiptStatusSuccessful { Fail(t, "l1receipt indicated failure") @@ -154,7 +154,7 @@ func TestSubmitRetryableFailThenRetry(t *testing.T) { waitForL1DelayBlocks(t, ctx, l1client, l1info) - receipt, err := arbnode.WaitForTx(ctx, l2client, *l2TxId, time.Second*5) + receipt, err := arbutil.WaitForTx(ctx, l2client, *l2TxId, time.Second*5) Require(t, err) if receipt.Status != types.ReceiptStatusSuccessful { Fail(t) @@ -166,7 +166,7 @@ func TestSubmitRetryableFailThenRetry(t *testing.T) { firstRetryTxId := receipt.Logs[1].Topics[2] // get receipt for the auto-redeem, make sure it failed - receipt, err = arbnode.WaitForTx(ctx, l2client, firstRetryTxId, time.Second*5) + receipt, err = arbutil.WaitForTx(ctx, l2client, firstRetryTxId, time.Second*5) Require(t, err) if receipt.Status != types.ReceiptStatusFailed { Fail(t, receipt.GasUsed) @@ -194,12 +194,12 @@ func TestSubmitRetryableFailThenRetry(t *testing.T) { l1tx, err = delayedInbox.SendL2Message(&usertxopts, txwrapped) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, l1client, l1tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) // wait for redeem transaction to complete successfully waitForL1DelayBlocks(t, ctx, l1client, l1info) - receipt, err = arbnode.EnsureTxSucceededWithTimeout(ctx, l2client, tx, time.Second*5) + receipt, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l2client, tx, time.Second*5) Require(t, err) if receipt.Status != types.ReceiptStatusSuccessful { Fail(t, *receipt) @@ -215,7 +215,7 @@ func TestSubmitRetryableFailThenRetry(t *testing.T) { } // check the receipt for the retry - receipt, err = arbnode.WaitForTx(ctx, l2client, retryTxId, time.Second*1) + receipt, err = arbutil.WaitForTx(ctx, l2client, retryTxId, time.Second*1) Require(t, err) if receipt.Status != 1 { Fail(t) @@ -259,7 +259,7 @@ func TestSubmissionGasCosts(t *testing.T) { ) Require(t, err) - l1receipt, err := arbnode.EnsureTxSucceeded(ctx, l1client, l1tx) + l1receipt, err := arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) Require(t, err) if l1receipt.Status != types.ReceiptStatusSuccessful { Fail(t, "l1receipt indicated failure") diff --git a/system_tests/seqfeed_test.go b/system_tests/seqfeed_test.go index 52fe1facac..d60d8e46ea 100644 --- a/system_tests/seqfeed_test.go +++ b/system_tests/seqfeed_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/broadcastclient" "github.com/offchainlabs/arbstate/wsbroadcastserver" ) @@ -58,10 +59,10 @@ func TestSequencerFeed(t *testing.T) { err := client1.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, client1, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client1, tx) Require(t, err) - _, err = arbnode.WaitForTx(ctx, client2, tx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, client2, tx.Hash(), time.Second*5) Require(t, err) l2balance, err := client2.BalanceAt(ctx, l2info1.GetAddress("User2"), nil) Require(t, err) @@ -110,13 +111,13 @@ func TestLyingSequencer(t *testing.T) { t.Fatal(err) } - _, err = arbnode.EnsureTxSucceeded(ctx, l2clientC, fraudTx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientC, fraudTx) if err != nil { t.Fatal(err) } // Node B should get the transaction immediately from the sequencer feed - _, err = arbnode.WaitForTx(ctx, l2clientB, fraudTx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2clientB, fraudTx.Hash(), time.Second*5) if err != nil { t.Fatal(err) } @@ -134,13 +135,13 @@ func TestLyingSequencer(t *testing.T) { t.Fatal(err) } - _, err = arbnode.EnsureTxSucceeded(ctx, l2clientA, realTx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, realTx) if err != nil { t.Fatal(err) } // Node B should get the transaction after NodeC posts a batch. - _, err = arbnode.WaitForTx(ctx, l2clientB, realTx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2clientB, realTx.Hash(), time.Second*5) if err != nil { t.Fatal(err) } diff --git a/system_tests/seqinbox_test.go b/system_tests/seqinbox_test.go index eda715b0bd..c39348030b 100644 --- a/system_tests/seqinbox_test.go +++ b/system_tests/seqinbox_test.go @@ -20,9 +20,9 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" ) @@ -109,7 +109,7 @@ func TestSequencerInboxReader(t *testing.T) { } tx := l1Info.SignTxAs("ReorgPadding", rawTx) Require(t, l1Client.SendTransaction(ctx, tx)) - _, _ = arbnode.EnsureTxSucceeded(ctx, l1Client, tx) + _, _ = arbutil.EnsureTxSucceeded(ctx, l1Client, tx) } reorgTargetNumber := blockStates[reorgTo].l1BlockNumber currentHeader, err := l1Client.HeaderByNumber(ctx, nil) @@ -130,7 +130,7 @@ func TestSequencerInboxReader(t *testing.T) { tx := l1Info.PrepareTx(fmt.Sprintf("ReorgSacrifice%v", i/10), "Faucet", 30000, big.NewInt(0), nil) err = l1Client.SendTransaction(ctx, tx) Require(t, err) - _, _ = arbnode.WaitForTx(ctx, l1Client, tx.Hash(), time.Second) + _, _ = arbutil.WaitForTx(ctx, l1Client, tx.Hash(), time.Second) } else { state := blockStates[len(blockStates)-1] newBalances := make(map[common.Address]*big.Int) @@ -214,7 +214,7 @@ func TestSequencerInboxReader(t *testing.T) { tx, err = seqInbox.AddSequencerL2BatchFromOrigin(&seqOpts, big.NewInt(int64(len(blockStates))), batchData, big.NewInt(1), common.Address{}) } Require(t, err) - txRes, err := arbnode.EnsureTxSucceeded(ctx, l1Client, tx) + txRes, err := arbutil.EnsureTxSucceeded(ctx, l1Client, tx) if err != nil { // Geth's clique miner is finicky. // Unfortunately this is so rare that I haven't had an opportunity to test this workaround. @@ -222,7 +222,7 @@ func TestSequencerInboxReader(t *testing.T) { // if a new tx arrives at the same time as it tries to create a block. // Resubmit the transaction in an attempt to get the miner going again. _ = l1Client.SendTransaction(ctx, tx) - txRes, err = arbnode.EnsureTxSucceeded(ctx, l1Client, tx) + txRes, err = arbutil.EnsureTxSucceeded(ctx, l1Client, tx) Require(t, err) } diff --git a/system_tests/transfer_test.go b/system_tests/transfer_test.go index a88ff0736b..74fd509425 100644 --- a/system_tests/transfer_test.go +++ b/system_tests/transfer_test.go @@ -10,7 +10,7 @@ import ( "math/big" "testing" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" ) func TestTransfer(t *testing.T) { @@ -25,7 +25,7 @@ func TestTransfer(t *testing.T) { err := client.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) bal, err := client.BalanceAt(ctx, l2info.GetAddress("Owner"), nil) diff --git a/system_tests/twonodes_test.go b/system_tests/twonodes_test.go index 332c98f9ee..a463e51bea 100644 --- a/system_tests/twonodes_test.go +++ b/system_tests/twonodes_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" ) func TestTwoNodesSimple(t *testing.T) { @@ -29,7 +29,7 @@ func TestTwoNodesSimple(t *testing.T) { err := l2clientA.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, l2clientA, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) // give the inbox reader a bit of time to pick up the delayed message @@ -42,7 +42,7 @@ func TestTwoNodesSimple(t *testing.T) { }) } - _, err = arbnode.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) Require(t, err) l2balance, err := l2clientB.BalanceAt(ctx, l2info.GetAddress("User2"), nil) diff --git a/system_tests/twonodeslong_test.go b/system_tests/twonodeslong_test.go index a1af03d231..e1e0ed084a 100644 --- a/system_tests/twonodeslong_test.go +++ b/system_tests/twonodeslong_test.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" ) func TestTwoNodesLong(t *testing.T) { @@ -99,7 +99,7 @@ func TestTwoNodesLong(t *testing.T) { SendWaitTestTransactions(t, ctx, l2client, l2Txs) directTransfers += int64(l2TxsThisTime) if len(l1Txs) > 0 { - _, err := arbnode.EnsureTxSucceeded(ctx, l1client, l1Txs[len(l1Txs)-1]) + _, err := arbutil.EnsureTxSucceeded(ctx, l1client, l1Txs[len(l1Txs)-1]) if err != nil { Fail(t, err) } @@ -133,15 +133,15 @@ func TestTwoNodesLong(t *testing.T) { Fail(t, err) } } - _, err := arbnode.EnsureTxSucceeded(ctx, l1client, tx) + _, err := arbutil.EnsureTxSucceeded(ctx, l1client, tx) if err != nil { Fail(t, err) } } - _, err = arbnode.EnsureTxSucceededWithTimeout(ctx, l2client, delayedTxs[len(delayedTxs)-1], time.Second*10) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l2client, delayedTxs[len(delayedTxs)-1], time.Second*10) Require(t, err, "Failed waiting for Tx on main node") - _, err = arbnode.EnsureTxSucceededWithTimeout(ctx, l2clientB, delayedTxs[len(delayedTxs)-1], time.Second*10) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l2clientB, delayedTxs[len(delayedTxs)-1], time.Second*10) Require(t, err, "Failed waiting for Tx on secondary node") delayedBalance, err := l2clientB.BalanceAt(ctx, l2info.GetAddress("DelayedReceiver"), nil) Require(t, err) diff --git a/system_tests/validator_test.go b/system_tests/validator_test.go index d40ec64f6f..a07f8c597d 100644 --- a/system_tests/validator_test.go +++ b/system_tests/validator_test.go @@ -15,7 +15,7 @@ import ( "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/arbutil" ) func TestValidatorSimple(t *testing.T) { @@ -33,7 +33,7 @@ func TestValidatorSimple(t *testing.T) { err := l2client.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, l2client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2client, tx) Require(t, err) SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ @@ -53,7 +53,7 @@ func TestValidatorSimple(t *testing.T) { // this is needed to stop the 1000000 balance error in CI (BUG) time.Sleep(time.Millisecond * 500) - _, err = arbnode.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) Require(t, err) // BUG: need to sleep to avoid (Unexpected balance: 1000000000000) diff --git a/validator/l1_validator.go b/validator/l1_validator.go new file mode 100644 index 0000000000..0c8a34a2c8 --- /dev/null +++ b/validator/l1_validator.go @@ -0,0 +1,475 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "encoding/hex" + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/math" + "github.com/offchainlabs/arbitrum/packages/arb-util/arbtransaction" + "github.com/offchainlabs/arbitrum/packages/arb-util/common" + "github.com/offchainlabs/arbitrum/packages/arb-util/core" + "github.com/offchainlabs/arbitrum/packages/arb-util/ethutils" + "github.com/offchainlabs/arbitrum/packages/arb-util/hashing" + "github.com/pkg/errors" +) + +type Validator struct { + rollup *ethbridge.Rollup + sequencerInbox *ethbridge.SequencerInboxWatcher + validatorUtils *ethbridge.ValidatorUtils + client ethutils.EthClient + builder *ethbridge.BuilderBackend + wallet *ethbridge.ValidatorWallet + GasThreshold *big.Int + SendThreshold *big.Int + BlockThreshold *big.Int +} + +func NewValidator( + ctx context.Context, + client ethutils.EthClient, + wallet *ethbridge.ValidatorWallet, + fromBlock int64, + validatorUtilsAddress common.Address, + callOpts bind.CallOpts, +) (*Validator, error) { + builder, err := ethbridge.NewBuilderBackend(wallet) + if err != nil { + return nil, err + } + rollup, err := ethbridge.NewRollup(wallet.RollupAddress().ToEthAddress(), fromBlock, client, builder, callOpts) + _ = rollup + if err != nil { + return nil, err + } + sequencerBridgeAddress, err := rollup.SequencerBridge(ctx) + if err != nil { + return nil, err + } + sequencerInbox, err := ethbridge.NewSequencerInboxWatcher(sequencerBridgeAddress.ToEthAddress(), client) + if err != nil { + return nil, err + } + validatorUtils, err := ethbridge.NewValidatorUtils( + validatorUtilsAddress.ToEthAddress(), + wallet.RollupAddress().ToEthAddress(), + client, + callOpts, + ) + if err != nil { + return nil, err + } + return &Validator{ + rollup: rollup, + sequencerInbox: sequencerInbox, + validatorUtils: validatorUtils, + client: client, + builder: builder, + wallet: wallet, + GasThreshold: big.NewInt(100_000_000_000), + SendThreshold: big.NewInt(5), + BlockThreshold: big.NewInt(960), + }, nil +} + +// removeOldStakers removes the stakes of all currently staked validators except +// its own if dontRemoveSelf is true +func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) (*arbtransaction.ArbTransaction, error) { + stakersToEliminate, err := v.validatorUtils.RefundableStakers(ctx) + if err != nil { + return nil, err + } + walletAddr := v.wallet.Address() + if dontRemoveSelf && walletAddr != nil { + for i, staker := range stakersToEliminate { + if staker.ToEthAddress() == *walletAddr { + stakersToEliminate[i] = stakersToEliminate[len(stakersToEliminate)-1] + stakersToEliminate = stakersToEliminate[:len(stakersToEliminate)-1] + break + } + } + } + + if len(stakersToEliminate) == 0 { + return nil, nil + } + logger.Info().Int("count", len(stakersToEliminate)).Msg("Removing old stakers") + return v.wallet.ReturnOldDeposits(ctx, stakersToEliminate) +} + +func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*arbtransaction.ArbTransaction, error) { + challengesToEliminate, err := v.validatorUtils.TimedOutChallenges(ctx, 10) + if err != nil { + return nil, err + } + if len(challengesToEliminate) == 0 { + return nil, nil + } + logger.Info().Int("count", len(challengesToEliminate)).Msg("Timing out challenges") + return v.wallet.TimeoutChallenges(ctx, challengesToEliminate) +} + +func (v *Validator) resolveNextNode(ctx context.Context, info *ethbridge.StakerInfo, fromBlock int64) error { + confirmType, err := v.validatorUtils.CheckDecidableNextNode(ctx) + if err != nil { + return err + } + unresolvedNodeIndex, err := v.rollup.FirstUnresolvedNode(ctx) + if err != nil { + return err + } + switch confirmType { + case ethbridge.CONFIRM_TYPE_INVALID: + addr := v.wallet.Address() + if info == nil || addr == nil || info.LatestStakedNode.Cmp(unresolvedNodeIndex) <= 0 { + // We aren't an example of someone staked on a competitor + return nil + } + logger.Info().Int("node", int(unresolvedNodeIndex.Int64())).Msg("Rejecting node") + return v.rollup.RejectNextNode(ctx, *addr) + case ethbridge.CONFIRM_TYPE_VALID: + nodeInfo, err := v.rollup.RollupWatcher.LookupNode(ctx, unresolvedNodeIndex) + if err != nil { + return err + } + sendCount := new(big.Int).Sub(nodeInfo.Assertion.After.TotalSendCount, nodeInfo.Assertion.Before.TotalSendCount) + sends, err := v.lookup.GetSends(nodeInfo.Assertion.Before.TotalSendCount, sendCount) + if err != nil { + return errors.Wrap(err, "catching up to chain") + } + logger.Info().Int("node", int(unresolvedNodeIndex.Int64())).Msg("Confirming node") + return v.rollup.ConfirmNextNode(ctx, nodeInfo.Assertion, sends) + default: + return nil + } +} + +func (v *Validator) isRequiredStakeElevated(ctx context.Context) (bool, error) { + requiredStake, err := v.rollup.CurrentRequiredStake(ctx) + if err != nil { + return false, err + } + baseStake, err := v.rollup.BaseStake(ctx) + if err != nil { + return false, err + } + return requiredStake.Cmp(baseStake) > 0, nil +} + +type createNodeAction struct { + assertion *core.Assertion + prevProposedBlock *big.Int + prevInboxMaxCount *big.Int + hash [32]byte + sequencerBatchProof []byte +} + +type existingNodeAction struct { + number *big.Int + hash [32]byte +} + +type nodeAction interface{} + +type OurStakerInfo struct { + LatestStakedNode *big.Int + LatestStakedNodeHash [32]byte + CanProgress bool + latestExecutionCursor core.ExecutionCursor + *ethbridge.StakerInfo +} + +func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy Strategy, fromBlock int64) (nodeAction, bool, error) { + startState, err := lookupNodeStartState(ctx, v.rollup.RollupWatcher, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) + if err != nil { + return nil, false, err + } + + coreMessageCount := v.lookup.MachineMessagesRead() + if coreMessageCount.Cmp(startState.TotalMessagesRead) < 0 { + logger.Info(). + Str("localcount", coreMessageCount.String()). + Str("target", startState.TotalMessagesRead.String()). + Msg("catching up to chain") + return nil, false, nil + } + + currentBlock, err := getBlockID(ctx, v.client, nil) + if err != nil { + return nil, false, err + } + + minAssertionPeriod, err := v.rollup.MinimumAssertionPeriod(ctx) + if err != nil { + return nil, false, err + } + + timeSinceProposed := new(big.Int).Sub(currentBlock.Height.AsInt(), startState.ProposedBlock) + if timeSinceProposed.Cmp(minAssertionPeriod) < 0 { + // Too soon to assert + return nil, false, nil + } + + cursor := stakerInfo.latestExecutionCursor + if cursor == nil || startState.TotalGasConsumed.Cmp(cursor.TotalGasConsumed()) < 0 { + cursor, err = v.lookup.GetExecutionCursor(startState.TotalGasConsumed, true) + if err != nil { + return nil, false, err + } + } else { + err = v.lookup.AdvanceExecutionCursor(cursor, new(big.Int).Sub(startState.TotalGasConsumed, cursor.TotalGasConsumed()), false, true) + if err != nil { + return nil, false, err + } + } + cursorHash := cursor.MachineHash() + if err != nil { + return nil, false, err + } + if cursorHash != startState.MachineHash { + return nil, false, errors.Errorf("local machine doesn't match chain %v %v", cursor.TotalGasConsumed(), startState.TotalGasConsumed) + } + + // Not necessarily successors + successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNodeHash, startState.ProposedBlock) + if err != nil { + return nil, false, err + } + + // If there are no successor nodes, and there isn't much activity to process, don't do anything yet + if len(successorNodes) == 0 { + coreGasExecuted, err := v.lookup.GetLastMachineTotalGas() + if err != nil { + return nil, false, err + } + coreSendCount, err := v.lookup.GetSendCount() + if err != nil { + return nil, false, err + } + gasExecuted := new(big.Int).Sub(coreGasExecuted, startState.TotalGasConsumed) + sendCount := new(big.Int).Sub(coreSendCount, startState.TotalSendCount) + if sendCount.Cmp(v.SendThreshold) < 0 && + gasExecuted.Cmp(v.GasThreshold) < 0 && + timeSinceProposed.Cmp(v.BlockThreshold) < 0 { + return nil, false, nil + } + } + + gasesUsed := make([]*big.Int, 0, len(successorNodes)+1) + for _, nd := range successorNodes { + gasesUsed = append(gasesUsed, nd.Assertion.After.TotalGasConsumed) + } + + arbGasSpeedLimitPerBlock, err := v.rollup.ArbGasSpeedLimitPerBlock(ctx) + if err != nil { + return nil, false, err + } + + minimumGasToConsume := new(big.Int).Mul(timeSinceProposed, arbGasSpeedLimitPerBlock) + maximumGasTarget := new(big.Int).Mul(minimumGasToConsume, big.NewInt(4)) + maximumGasTarget = maximumGasTarget.Add(maximumGasTarget, startState.TotalGasConsumed) + + if strategy > WatchtowerStrategy { + gasesUsed = append(gasesUsed, maximumGasTarget) + } + + execTracker := core.NewExecutionTrackerWithInitialCursor(v.lookup, false, gasesUsed, cursor, false) + + var correctNode nodeAction + wrongNodesExist := false + if len(successorNodes) > 0 { + logger.Info().Int("count", len(successorNodes)).Msg("examining existing potential successors") + } + for nodeI, nd := range successorNodes { + if correctNode != nil && wrongNodesExist { + // We've found everything we could hope to find + break + } + if correctNode == nil { + var batchItemEndAcc common.Hash + if nd.Assertion.After.TotalMessagesRead.Cmp(nd.AfterInboxBatchEndCount) == 0 { + batchItemEndAcc = nd.AfterInboxBatchAcc + } else if nd.Assertion.After.TotalMessagesRead.Cmp(big.NewInt(0)) > 0 { + var haveBatchEndAcc common.Hash + index1 := new(big.Int).Sub(nd.Assertion.After.TotalMessagesRead, big.NewInt(1)) + index2 := new(big.Int).Sub(nd.AfterInboxBatchEndCount, big.NewInt(1)) + batchItemEndAcc, haveBatchEndAcc, err = v.lookup.GetInboxAccPair(index1, index2) + if err != nil { + return nil, false, err + } + if haveBatchEndAcc != nd.AfterInboxBatchAcc { + return nil, false, errors.New("inbox reorg detected by batch end acc mismatch") + } + } + valid, err := core.IsAssertionValid(nd.Assertion, execTracker, batchItemEndAcc) + if err != nil { + return nil, false, err + } + if valid { + logger.Info().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found correct node") + correctNode = existingNodeAction{ + number: nd.NodeNum, + hash: nd.NodeHash, + } + stakerInfo.latestExecutionCursor, err = execTracker.GetExecutionCursor(nd.AfterState().TotalGasConsumed, true) + if err != nil { + return nil, false, err + } + if nodeI != len(successorNodes)-1 && stakerInfo.latestExecutionCursor != nil { + // We will need to use this execution tracker more, so we need to clone this cursor + stakerInfo.latestExecutionCursor = stakerInfo.latestExecutionCursor.Clone() + } + continue + } else { + logger.Warn().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found node with incorrect assertion") + } + } else { + logger.Warn().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found younger sibling to correct node") + } + // If we've hit this point, the node is "wrong" + wrongNodesExist = true + } + + if strategy == WatchtowerStrategy || correctNode != nil || (strategy < MakeNodesStrategy && !wrongNodesExist) { + return correctNode, wrongNodesExist, nil + } + + execState, _, err := execTracker.GetExecutionState(maximumGasTarget) + if err != nil { + return nil, false, err + } + stakerInfo.latestExecutionCursor, err = execTracker.GetExecutionCursor(maximumGasTarget, true) + if err != nil { + return nil, false, err + } + + if new(big.Int).Sub(execState.TotalGasConsumed, startState.TotalGasConsumed).Cmp(minimumGasToConsume) < 0 && execState.TotalMessagesRead.Cmp(startState.InboxMaxCount) < 0 { + // Couldn't execute far enough + return nil, wrongNodesExist, nil + } + + inboxAcc := execState.InboxAcc + hasSiblingByte := [1]byte{0} + lastNum := stakerInfo.LatestStakedNode + lastHash := stakerInfo.LatestStakedNodeHash + if len(successorNodes) > 0 { + lastSuccessor := successorNodes[len(successorNodes)-1] + lastNum = lastSuccessor.NodeNum + lastHash = lastSuccessor.NodeHash + hasSiblingByte[0] = 1 + } + assertion := &core.Assertion{ + Before: startState.ExecutionState, + After: execState, + } + + executionHash := assertion.ExecutionHash() + newNodeHash := hashing.SoliditySHA3(hasSiblingByte[:], lastHash[:], executionHash[:], inboxAcc[:]) + + var seqBatchProof []byte + if execState.TotalMessagesRead.Cmp(big.NewInt(0)) > 0 { + batch, err := v.sequencerInbox.LookupBatchContaining(ctx, v.lookup, new(big.Int).Sub(execState.TotalMessagesRead, big.NewInt(1))) + if err != nil { + return nil, false, err + } + if batch == nil { + return nil, false, errors.New("Failed to lookup batch containing message") + } + seqBatchProof = append(seqBatchProof, math.U256Bytes(batch.GetBatchIndex())...) + proofPart, err := v.generateBatchEndProof(batch.GetBeforeCount()) + if err != nil { + return nil, false, err + } + seqBatchProof = append(seqBatchProof, proofPart...) + proofPart, err = v.generateBatchEndProof(batch.GetAfterCount()) + if err != nil { + return nil, false, err + } + seqBatchProof = append(seqBatchProof, proofPart...) + } + + action := createNodeAction{ + assertion: assertion, + hash: newNodeHash, + prevProposedBlock: startState.ProposedBlock, + prevInboxMaxCount: startState.InboxMaxCount, + sequencerBatchProof: seqBatchProof, + } + logger.Info().Str("hash", hex.EncodeToString(newNodeHash[:])).Int("lastNode", int(lastNum.Int64())).Int("parentNode", int(stakerInfo.LatestStakedNode.Int64())).Msg("Creating node") + return action, wrongNodesExist, nil +} + +func (v *Validator) generateBatchEndProof(count *big.Int) ([]byte, error) { + if count.Cmp(big.NewInt(0)) == 0 { + return []byte{}, nil + } + var beforeAcc common.Hash + var err error + if count.Cmp(big.NewInt(2)) >= 0 { + beforeAcc, err = v.lookup.GetInboxAcc(new(big.Int).Sub(count, big.NewInt(2))) + if err != nil { + return nil, err + } + } + seqNum := new(big.Int).Sub(count, big.NewInt(1)) + message, err := core.GetSingleMessage(v.lookup, seqNum) + if err != nil { + return nil, err + } + var proof []byte + proof = append(proof, beforeAcc.Bytes()...) + proof = append(proof, math.U256Bytes(seqNum)...) + prefixHash := hashing.SoliditySHA3( + hashing.Address(message.Sender), + hashing.Uint256(message.ChainTime.BlockNum.AsInt()), + hashing.Uint256(message.ChainTime.Timestamp), + ) + proof = append(proof, prefixHash.Bytes()...) + proof = append(proof, hashing.SoliditySHA3(message.Data).Bytes()...) + return proof, nil +} + +func getBlockID(ctx context.Context, client ethutils.EthClient, number *big.Int) (*common.BlockId, error) { + blockInfo, err := client.BlockInfoByNumber(ctx, number) + if err != nil { + return nil, err + } + return &common.BlockId{ + Height: common.NewTimeBlocks((*big.Int)(blockInfo.Number)), + HeaderHash: common.NewHashFromEth(blockInfo.Hash), + }, nil +} + +func lookupNodeStartState(ctx context.Context, rollup *ethbridge.RollupWatcher, nodeNum *big.Int, nodeHash [32]byte) (*core.NodeState, error) { + if nodeNum.Cmp(big.NewInt(0)) == 0 { + creationEvent, err := rollup.LookupCreation(ctx) + if err != nil { + return nil, err + } + return &core.NodeState{ + ProposedBlock: new(big.Int).SetUint64(creationEvent.Raw.BlockNumber), + InboxMaxCount: big.NewInt(1), + ExecutionState: &core.ExecutionState{ + TotalGasConsumed: big.NewInt(0), + MachineHash: creationEvent.MachineHash, + TotalMessagesRead: big.NewInt(0), + TotalSendCount: big.NewInt(0), + TotalLogCount: big.NewInt(0), + }, + }, nil + } + node, err := rollup.LookupNode(ctx, nodeNum) + if err != nil { + return nil, err + } + if node.NodeHash != nodeHash { + return nil, errors.New("looked up starting node but found wrong hash") + } + return node.AfterState(), nil +} diff --git a/validator/staker.go b/validator/staker.go new file mode 100644 index 0000000000..f05f4e106c --- /dev/null +++ b/validator/staker.go @@ -0,0 +1,475 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/validator" + "github.com/pkg/errors" +) + +type Strategy uint8 + +const ( + WatchtowerStrategy Strategy = iota + DefensiveStrategy + StakeLatestStrategy + MakeNodesStrategy +) + +type nodeAndHash struct { + id *big.Int + hash common.Hash +} + +type StakerInfo struct { + Index *big.Int + LatestStakedNode *big.Int + AmountStaked *big.Int + CurrentChallenge *common.Address +} + +type Staker struct { + *Validator + activeChallenge *validator.ChallengeManager + strategy Strategy + fromBlock int64 + baseCallOpts bind.CallOpts + auth *bind.TransactOpts + config ValidatorConfig + highGasBlocksBuffer *big.Int + lastActCalledBlock *big.Int + inactiveLastCheckedNode *nodeAndHash + bringActiveUntilNode *big.Int + withdrawDestination common.Address +} + +func NewStaker( + ctx context.Context, + client *ethclient.Client, + wallet *ethbridge.ValidatorWallet, + fromBlock int64, + validatorUtilsAddress common.Address, + strategy Strategy, + callOpts bind.CallOpts, + auth *bind.TransactOpts, + config ValidatorConfig, +) (*Staker, error) { + val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts) + if err != nil { + return nil, err + } + withdrawDestination := wallet.From() + if common.IsHexAddress(config.WithdrawDestination) { + withdrawDestination = common.HexToAddress(config.WithdrawDestination) + } + return &Staker{ + Validator: val, + strategy: strategy, + fromBlock: fromBlock, + baseCallOpts: callOpts, + auth: auth, + config: config, + highGasBlocksBuffer: big.NewInt(config.L1PostingStrategy.HighGasDelayBlocks), + lastActCalledBlock: nil, + withdrawDestination: withdrawDestination, + }, nil +} + +func (s *Staker) RunInBackground(ctx context.Context, stakerDelay time.Duration) chan bool { + done := make(chan bool) + go func() { + defer func() { + done <- true + }() + backoff := time.Second + for { + arbTx, err := s.Act(ctx) + if err == nil && arbTx != nil { + // Note: methodName isn't accurate, it's just used for logging + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, s.client, arbTx, 5*time.Minute) + err = errors.Wrap(err, "error waiting for tx receipt") + if err == nil { + log.Info("successfully executed staker transaction", "hash", arbTx.Hash()) + } + } + if err != nil { + log.Warn("error acting as staker", "err", err) + select { + case <-ctx.Done(): + return + case <-time.After(backoff): + } + if backoff < 60*time.Second { + backoff *= 2 + } + continue + } else { + backoff = time.Second + } + time.Sleep(stakerDelay) + } + }() + return done +} + +func (s *Staker) shouldAct(ctx context.Context) bool { + var gasPriceHigh = false + var gasPriceFloat float64 + gasPrice, err := s.client.SuggestGasPrice(ctx) + if err != nil { + log.Warn("error getting gas price", "err", err) + } else { + gasPriceFloat = float64(gasPrice.Int64()) / 1e9 + if gasPriceFloat >= s.config.L1PostingStrategy.HighGasThreshold { + gasPriceHigh = true + } + } + latestBlockInfo, err := s.client.BlockInfoByNumber(ctx, nil) + if err != nil { + log.Warn("error getting latest block", "err", err) + return true + } + latestBlockNum := latestBlockInfo.Number.ToInt() + if s.lastActCalledBlock == nil { + s.lastActCalledBlock = latestBlockNum + } + blocksSinceActCalled := new(big.Int).Sub(latestBlockNum, s.lastActCalledBlock) + s.lastActCalledBlock = latestBlockNum + if gasPriceHigh { + // We're eating into the high gas buffer to delay our tx + s.highGasBlocksBuffer.Sub(s.highGasBlocksBuffer, blocksSinceActCalled) + } else { + // We'll make a tx if necessary, so we can add to the buffer for future high gas + s.highGasBlocksBuffer.Add(s.highGasBlocksBuffer, blocksSinceActCalled) + } + // Clamp `s.highGasBlocksBuffer` to between 0 and HighGasDelayBlocks + if s.highGasBlocksBuffer.Sign() < 0 { + s.highGasBlocksBuffer.SetInt64(0) + } else if s.highGasBlocksBuffer.Cmp(big.NewInt(s.config.L1PostingStrategy.HighGasDelayBlocks)) > 0 { + s.highGasBlocksBuffer.SetInt64(s.config.L1PostingStrategy.HighGasDelayBlocks) + } + if gasPriceHigh && s.highGasBlocksBuffer.Sign() > 0 { + log.Warn( + "not acting yet as gas price is high", + "gasPrice", gasPriceFloat, + "highGasPriceConfig", s.config.L1PostingStrategy.HighGasThreshold, + "highGasBuffer", s.highGasBlocksBuffer, + ) + return false + } else { + return true + } +} + +func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { + if !s.shouldAct(ctx) { + // The fact that we're delaying acting is alreay logged in `shouldAct` + return nil, nil + } + s.builder.ClearTransactions() + var rawInfo *StakerInfo + walletAddress := s.wallet.Address() + var walletAddressOrZero common.Address + if walletAddress != nil { + walletAddressOrZero = *walletAddress + } + if walletAddress != nil { + var err error + rawInfo, err = s.rollup.StakerInfo(ctx, walletAddressOrZero) + if err != nil { + return nil, err + } + } + // If the wallet address is zero, or the wallet address isn't staked, + // this will return the latest node and its hash (atomically). + latestStakedNode, latestStakedNodeHash, err := s.validatorUtils.LatestStaked(ctx, walletAddressOrZero) + if err != nil { + return nil, err + } + if rawInfo != nil { + rawInfo.LatestStakedNode = latestStakedNode + } + info := OurStakerInfo{ + CanProgress: true, + LatestStakedNode: latestStakedNode, + LatestStakedNodeHash: latestStakedNodeHash, + StakerInfo: rawInfo, + } + + effectiveStrategy := s.strategy + nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(ctx) + if err != nil { + return nil, err + } + if !nodesLinear { + log.Warn("fork detected") + if effectiveStrategy == DefensiveStrategy { + effectiveStrategy = StakeLatestStrategy + } + s.inactiveLastCheckedNode = nil + } + if s.bringActiveUntilNode != nil { + if info.LatestStakedNode.Cmp(s.bringActiveUntilNode) < 0 { + if effectiveStrategy == DefensiveStrategy { + effectiveStrategy = StakeLatestStrategy + } + } else { + log.Info("defensive validator staked past incorrect node; waiting here") + s.bringActiveUntilNode = nil + } + s.inactiveLastCheckedNode = nil + } + if effectiveStrategy <= DefensiveStrategy && s.inactiveLastCheckedNode != nil { + info.LatestStakedNode = s.inactiveLastCheckedNode.id + info.LatestStakedNodeHash = s.inactiveLastCheckedNode.hash + } + + // Resolve nodes if either we're on the make nodes strategy, + // or we're on the stake latest strategy but don't have a stake + // (attempt to reduce the current required stake). + shouldResolveNodes := effectiveStrategy >= MakeNodesStrategy + if !shouldResolveNodes && effectiveStrategy >= StakeLatestStrategy && rawInfo == nil { + shouldResolveNodes, err = s.isRequiredStakeElevated(ctx) + if err != nil { + return nil, err + } + } + if shouldResolveNodes { + // Keep the stake of this validator placed if we plan on staking further + arbTx, err := s.removeOldStakers(ctx, effectiveStrategy >= StakeLatestStrategy) + if err != nil || arbTx != nil { + return arbTx, err + } + arbTx, err = s.resolveTimedOutChallenges(ctx) + if err != nil || arbTx != nil { + return arbTx, err + } + if err := s.resolveNextNode(ctx, rawInfo, s.fromBlock); err != nil { + return nil, err + } + } + + addr := s.wallet.Address() + if addr != nil { + withdrawable, err := s.rollup.WithdrawableFunds(ctx, *addr) + if err != nil { + return nil, err + } + if withdrawable.Sign() > 0 && s.withdrawDestination != (common.Address{}) { + err = s.rollup.WithdrawFunds(ctx, s.withdrawDestination) + if err != nil { + return nil, err + } + } + } + + // Don't attempt to create a new stake if we're resolving a node, + // as that might affect the current required stake. + creatingNewStake := rawInfo == nil && s.builder.TransactionCount() == 0 + if creatingNewStake { + if err := s.newStake(ctx); err != nil { + return nil, err + } + } + + if rawInfo != nil { + if err = s.handleConflict(ctx, rawInfo); err != nil { + return nil, err + } + } + if rawInfo != nil || creatingNewStake { + // Advance stake up to 20 times in one transaction + for i := 0; info.CanProgress && i < 20; i++ { + if err := s.advanceStake(ctx, &info, effectiveStrategy); err != nil { + return nil, err + } + } + } + if rawInfo != nil && s.builder.TransactionCount() == 0 { + if err := s.createConflict(ctx, rawInfo); err != nil { + return nil, err + } + } + + txCount := s.builder.TransactionCount() + if creatingNewStake { + // Ignore our stake creation, as it's useless by itself + txCount-- + } + if txCount == 0 { + return nil, nil + } + if creatingNewStake { + log.Info("staking to execute transactions") + } + return s.wallet.ExecuteTransactions(ctx, s.builder) +} + +func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { + if info.CurrentChallenge == nil { + s.activeChallenge = nil + return nil + } + + if s.activeChallenge == nil || s.activeChallenge.ChallengeAddress() != *info.CurrentChallenge { + log.Warn("entered challenge", "challenge", info.CurrentChallenge) + + newChallengeManager, err := validator.NewChallengeManager(ctx, s.client, s.auth, *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, s.fromBlock, s.config.TargetNumMachines) + if err != nil { + return err + } + + s.activeChallenge = newChallengeManager + } + + _, err := s.activeChallenge.Act(ctx) + return err +} + +func (s *Staker) newStake(ctx context.Context) error { + var addr = s.wallet.Address() + if addr != nil { + info, err := s.rollup.StakerInfo(ctx, *addr) + if err != nil { + return err + } + if info != nil { + return nil + } + } + stakeAmount, err := s.rollup.CurrentRequiredStake(ctx) + if err != nil { + return err + } + return s.rollup.NewStake(ctx, stakeAmount) +} + +func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy Strategy) error { + active := effectiveStrategy >= StakeLatestStrategy + action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, s.fromBlock) + if err != nil { + return err + } + if wrongNodesExist && effectiveStrategy == WatchtowerStrategy { + log.Error("found incorrect assertion in watchtower mode") + } + if action == nil { + info.CanProgress = false + return nil + } + + switch action := action.(type) { + case createNodeAction: + if wrongNodesExist && s.config.DontChallenge { + log.Error("refusing to challenge assertion as config disables challenges") + return nil + } + if !active { + if wrongNodesExist && effectiveStrategy >= DefensiveStrategy { + log.Warn("bringing defensive validator online because of incorrect assertion") + s.bringActiveUntilNode = new(big.Int).Add(info.LatestStakedNode, big.NewInt(1)) + } + info.CanProgress = false + return nil + } + // Details are already logged with more details in generateNodeAction + info.CanProgress = false + info.LatestStakedNode = nil + info.LatestStakedNodeHash = action.hash + return s.rollup.StakeOnNewNode(ctx, action.hash, action.assertion, action.prevProposedBlock, action.prevInboxMaxCount, action.sequencerBatchProof) + case existingNodeAction: + info.LatestStakedNode = action.number + info.LatestStakedNodeHash = action.hash + if !active { + if wrongNodesExist && effectiveStrategy >= DefensiveStrategy { + log.Warn("bringing defensive validator online because of incorrect assertion") + s.bringActiveUntilNode = action.number + info.CanProgress = false + } else { + s.inactiveLastCheckedNode = &nodeAndHash{ + id: action.number, + hash: action.hash, + } + } + return nil + } + log.Info("staking on existing node", "node", action.number) + return s.rollup.StakeOnExistingNode(ctx, action.number, action.hash) + default: + panic("invalid action type") + } +} + +func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { + if info.CurrentChallenge != nil { + return nil + } + + stakers, err := s.validatorUtils.GetStakers(ctx) + if err != nil { + return err + } + latestNode, err := s.rollup.LatestConfirmedNode(ctx) + if err != nil { + return err + } + // Safe to dereference as createConflict is only called when we have a wallet address + walletAddr := *s.wallet.Address() + for _, staker := range stakers { + stakerInfo, err := s.rollup.StakerInfo(ctx, staker) + if err != nil { + return err + } + if stakerInfo.CurrentChallenge != nil { + continue + } + conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(ctx, walletAddr, staker) + if err != nil { + return err + } + if conflictType != CONFLICT_TYPE_FOUND { + continue + } + staker1 := walletAddr + staker2 := staker + if node2.Cmp(node1) < 0 { + staker1, staker2 = staker2, staker1 + node1, node2 = node2, node1 + } + if node1.Cmp(latestNode) <= 0 { + // removeOldStakers will take care of them + continue + } + + node1Info, err := s.rollup.RollupWatcher.LookupNode(ctx, node1) + if err != nil { + return err + } + node2Info, err := s.rollup.RollupWatcher.LookupNode(ctx, node2) + if err != nil { + return err + } + log.Warn("creating challenge", "ourNode", node1, "otherNode", node2, "otherStaker", staker2) + return s.rollup.CreateChallenge( + ctx, + staker1, + node1Info, + staker2, + node2Info, + ) + } + // No conflicts exist + return nil +} From 1e696e5dd0bb5310a6539e44d99c318f09b161ee Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 1 Feb 2022 18:31:15 -0600 Subject: [PATCH 002/110] Add ValidatorWallet, ValidatorWalletCreator, and ValidatorUtils contracts --- solgen/src/challenge/BlockChallenge.sol | 2 +- solgen/src/challenge/ChallengeCore.sol | 13 +- solgen/src/challenge/ExecutionChallenge.sol | 2 +- solgen/src/challenge/IChallenge.sol | 6 + solgen/src/rollup/ValidatorUtils.sol | 276 +++++++++++++++++++ solgen/src/rollup/ValidatorWallet.sol | 99 +++++++ solgen/src/rollup/ValidatorWalletCreator.sol | 57 ++++ validator/builderbackend.go | 132 +++++++++ validator/l1_validator.go | 2 +- validator/staker.go | 15 +- validator/validator_wallet.go | 235 ++++++++++++++++ 11 files changed, 823 insertions(+), 16 deletions(-) create mode 100644 solgen/src/rollup/ValidatorUtils.sol create mode 100644 solgen/src/rollup/ValidatorWallet.sol create mode 100644 solgen/src/rollup/ValidatorWalletCreator.sol create mode 100644 validator/builderbackend.go create mode 100644 validator/validator_wallet.go diff --git a/solgen/src/challenge/BlockChallenge.sol b/solgen/src/challenge/BlockChallenge.sol index 2495d0640f..06ba75b139 100644 --- a/solgen/src/challenge/BlockChallenge.sol +++ b/solgen/src/challenge/BlockChallenge.sol @@ -10,7 +10,7 @@ import "./ChallengeCore.sol"; import "./IChallenge.sol"; import "./IExecutionChallengeFactory.sol"; -contract BlockChallenge is ChallengeCore, IChallengeResultReceiver, IChallenge { +contract BlockChallenge is ChallengeCore, IChallengeResultReceiver { event ExecutionChallengeBegun(IChallenge indexed challenge, uint256 blockSteps); IExecutionChallengeFactory public executionChallengeFactory; diff --git a/solgen/src/challenge/ChallengeCore.sol b/solgen/src/challenge/ChallengeCore.sol index db8b0a5bdd..43e00bf28a 100644 --- a/solgen/src/challenge/ChallengeCore.sol +++ b/solgen/src/challenge/ChallengeCore.sol @@ -4,8 +4,9 @@ pragma solidity ^0.8.0; import "../libraries/Cloneable.sol"; import "./ChallengeLib.sol"; import "./IChallengeResultReceiver.sol"; +import "./IChallenge.sol"; -abstract contract ChallengeCore is Cloneable { +abstract contract ChallengeCore is Cloneable, IChallenge { event InitiatedChallenge(); enum Turn { @@ -23,12 +24,12 @@ abstract contract ChallengeCore is Cloneable { event AsserterTimedOut(); event ChallengerTimedOut(); - address public asserter; - address public challenger; + address public override asserter; + address public override challenger; uint256 public asserterTimeLeft; uint256 public challengerTimeLeft; - uint256 public lastMoveTimestamp; + uint256 public override lastMoveTimestamp; Turn public turn; bytes32 public challengeStateHash; @@ -67,7 +68,7 @@ abstract contract ChallengeCore is Cloneable { } } - function currentResponderTimeLeft() public view returns (uint256) { + function currentResponderTimeLeft() public override view returns (uint256) { if (turn == Turn.ASSERTER) { return asserterTimeLeft; } else if (turn == Turn.CHALLENGER) { @@ -158,7 +159,7 @@ abstract contract ChallengeCore is Cloneable { ); } - function timeout() external { + function timeout() external override { uint256 timeSinceLastMove = block.timestamp - lastMoveTimestamp; require( timeSinceLastMove > currentResponderTimeLeft(), diff --git a/solgen/src/challenge/ExecutionChallenge.sol b/solgen/src/challenge/ExecutionChallenge.sol index ce476040ed..f5940e3f14 100644 --- a/solgen/src/challenge/ExecutionChallenge.sol +++ b/solgen/src/challenge/ExecutionChallenge.sol @@ -10,7 +10,7 @@ import "./IChallenge.sol"; import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -contract ExecutionChallenge is ChallengeCore, IChallenge { +contract ExecutionChallenge is ChallengeCore { event OneStepProofCompleted(); IOneStepProofEntry public osp; diff --git a/solgen/src/challenge/IChallenge.sol b/solgen/src/challenge/IChallenge.sol index a24f83e702..98e6bd2143 100644 --- a/solgen/src/challenge/IChallenge.sol +++ b/solgen/src/challenge/IChallenge.sol @@ -2,5 +2,11 @@ pragma solidity ^0.8.0; interface IChallenge { + function asserter() external view returns (address); + function challenger() external view returns (address); + function lastMoveTimestamp() external view returns (uint256); + function currentResponderTimeLeft() external view returns (uint256); + function clearChallenge() external; + function timeout() external; } diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol new file mode 100644 index 0000000000..3531114c71 --- /dev/null +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pragma solidity ^0.8.0; + +pragma experimental ABIEncoderV2; + +import "../rollup/Rollup.sol"; +import "../challenge/IChallenge.sol"; + +contract ValidatorUtils { + using NodeLib for Node; + + enum ConfirmType { + NONE, + VALID, + INVALID + } + + enum NodeConflict { + NONE, + FOUND, + INDETERMINATE, + INCOMPLETE + } + + function stakerInfo(Rollup rollup, address stakerAddress) + external + view + returns ( + bool isStaked, + uint256 latestStakedNode, + uint256 amountStaked, + IChallenge currentChallenge + ) + { + return ( + rollup.isStaked(stakerAddress), + rollup.latestStakedNode(stakerAddress), + rollup.amountStaked(stakerAddress), + rollup.currentChallenge(stakerAddress) + ); + } + + function findStakerConflict( + Rollup rollup, + address staker1, + address staker2, + uint256 maxDepth + ) + external + view + returns ( + NodeConflict, + uint64, + uint64 + ) + { + uint64 staker1NodeNum = rollup.latestStakedNode(staker1); + uint64 staker2NodeNum = rollup.latestStakedNode(staker2); + return findNodeConflict(rollup, staker1NodeNum, staker2NodeNum, maxDepth); + } + + function checkDecidableNextNode(Rollup rollup) external view returns (ConfirmType) { + try ValidatorUtils(address(this)).requireConfirmable(rollup) { + return ConfirmType.VALID; + } catch {} + + try ValidatorUtils(address(this)).requireRejectable(rollup) { + return ConfirmType.INVALID; + } catch { + return ConfirmType.NONE; + } + } + + function requireRejectable(Rollup rollup) external view returns (bool) { + IRollupUser(address(rollup)).requireUnresolvedExists(); + uint64 firstUnresolvedNode = rollup.firstUnresolvedNode(); + Node memory node = rollup.getNode(firstUnresolvedNode); + bool inOrder = node.prevNum == rollup.latestConfirmed(); + if (inOrder) { + // Verify the block's deadline has passed + require(block.number >= node.deadlineBlock, "BEFORE_DEADLINE"); + rollup.getNode(node.prevNum).requirePastChildConfirmDeadline(); + + // Verify that no staker is staked on this node + require( + node.stakerCount == IRollupUser(address(rollup)).countStakedZombies(firstUnresolvedNode), + "HAS_STAKERS" + ); + } + return inOrder; + } + + function requireConfirmable(Rollup rollup) external view { + IRollupUser(address(rollup)).requireUnresolvedExists(); + + uint256 stakerCount = rollup.stakerCount(); + // There is at least one non-zombie staker + require(stakerCount > 0, "NO_STAKERS"); + + uint64 firstUnresolved = rollup.firstUnresolvedNode(); + Node memory node = rollup.getNode(firstUnresolved); + + // Verify the block's deadline has passed + node.requirePastDeadline(); + rollup.getNode(node.prevNum).requirePastChildConfirmDeadline(); + + // Check that prev is latest confirmed + require(node.prevNum == rollup.latestConfirmed(), "INVALID_PREV"); + require( + node.stakerCount == + stakerCount + IRollupUser(address(rollup)).countStakedZombies(firstUnresolved), + "NOT_ALL_STAKED" + ); + } + + function refundableStakers(Rollup rollup) external view returns (address[] memory) { + uint256 stakerCount = rollup.stakerCount(); + address[] memory stakers = new address[](stakerCount); + uint256 latestConfirmed = rollup.latestConfirmed(); + uint256 index = 0; + for (uint64 i = 0; i < stakerCount; i++) { + address staker = rollup.getStakerAddress(i); + uint256 latestStakedNode = rollup.latestStakedNode(staker); + if ( + latestStakedNode <= latestConfirmed && rollup.currentChallenge(staker) == IChallenge(address(0)) + ) { + stakers[index] = staker; + index++; + } + } + assembly { + mstore(stakers, index) + } + return stakers; + } + + function latestStaked(Rollup rollup, address staker) external view returns (uint64, bytes32) { + uint64 node = rollup.latestStakedNode(staker); + if (node == 0) { + node = rollup.latestConfirmed(); + } + bytes32 acc = rollup.getNodeHash(node); + return (node, acc); + } + + function stakedNodes(Rollup rollup, address staker) external view returns (uint64[] memory) { + uint64[] memory nodes = new uint64[](100000); + uint256 index = 0; + for (uint64 i = rollup.latestConfirmed(); i <= rollup.latestNodeCreated(); i++) { + if (rollup.nodeHasStaker(i, staker)) { + nodes[index] = i; + index++; + } + } + // Shrink array down to real size + assembly { + mstore(nodes, index) + } + return nodes; + } + + function findNodeConflict( + Rollup rollup, + uint64 node1, + uint64 node2, + uint256 maxDepth + ) + public + view + returns ( + NodeConflict, + uint64, + uint64 + ) + { + uint64 firstUnresolvedNode = rollup.firstUnresolvedNode(); + uint64 node1Prev = rollup.getNode(node1).prevNum; + uint64 node2Prev = rollup.getNode(node2).prevNum; + + for (uint256 i = 0; i < maxDepth; i++) { + if (node1 == node2) { + return (NodeConflict.NONE, node1, node2); + } + if (node1Prev == node2Prev) { + return (NodeConflict.FOUND, node1, node2); + } + if (node1Prev < firstUnresolvedNode && node2Prev < firstUnresolvedNode) { + return (NodeConflict.INDETERMINATE, 0, 0); + } + if (node1Prev < node2Prev) { + node2 = node2Prev; + node2Prev = rollup.getNode(node2).prevNum; + } else { + node1 = node1Prev; + node1Prev = rollup.getNode(node1).prevNum; + } + } + return (NodeConflict.INCOMPLETE, node1, node2); + } + + function getStakers( + Rollup rollup, + uint64 startIndex, + uint64 max + ) public view returns (address[] memory, bool hasMore) { + uint256 maxStakers = rollup.stakerCount(); + if (startIndex + max <= maxStakers) { + maxStakers = startIndex + max; + hasMore = true; + } + + address[] memory stakers = new address[](maxStakers); + for (uint64 i = 0; i < maxStakers; i++) { + stakers[i] = rollup.getStakerAddress(startIndex + i); + } + return (stakers, hasMore); + } + + function timedOutChallenges( + Rollup rollup, + uint64 startIndex, + uint64 max + ) external view returns (IChallenge[] memory, bool hasMore) { + (address[] memory stakers, bool hasMoreStakers) = getStakers(rollup, startIndex, max); + IChallenge[] memory challenges = new IChallenge[](stakers.length); + uint256 index = 0; + for (uint256 i = 0; i < stakers.length; i++) { + address staker = stakers[i]; + IChallenge challengeAddr = rollup.currentChallenge(staker); + if (challengeAddr != IChallenge(address(0))) { + IChallenge challenge = IChallenge(challengeAddr); + uint256 timeSinceLastMove = block.timestamp - challenge.lastMoveTimestamp(); + if ( + timeSinceLastMove > challenge.currentResponderTimeLeft() && + challenge.asserter() == staker + ) { + challenges[index] = challenge; + index++; + } + } + } + // Shrink array down to real size + assembly { + mstore(challenges, index) + } + return (challenges, hasMoreStakers); + } + + // Worst case runtime of O(depth), as it terminates if it switches paths. + function areUnresolvedNodesLinear(Rollup rollup) external view returns (bool) { + uint256 end = rollup.latestNodeCreated(); + for (uint64 i = rollup.firstUnresolvedNode(); i <= end; i++) { + if (i > 0 && rollup.getNode(i).prevNum != i - 1) { + return false; + } + } + return true; + } +} diff --git a/solgen/src/rollup/ValidatorWallet.sol b/solgen/src/rollup/ValidatorWallet.sol new file mode 100644 index 0000000000..fc68eb545e --- /dev/null +++ b/solgen/src/rollup/ValidatorWallet.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pragma solidity ^0.8.0; + +import "./IRollupLogic.sol"; +import "../challenge/IChallenge.sol"; +import "../libraries/Cloneable.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract ValidatorWallet is OwnableUpgradeable, Cloneable { + using Address for address; + + function initialize() external initializer { + __Ownable_init(); + } + + function executeTransactions( + bytes[] calldata data, + address[] calldata destination, + uint256[] calldata amount + ) external payable onlyOwner { + uint256 numTxes = data.length; + for (uint256 i = 0; i < numTxes; i++) { + if (data[i].length > 0) require(destination[i].isContract(), "NO_CODE_AT_ADDR"); + (bool success, ) = address(destination[i]).call{ value: amount[i] }(data[i]); + if (!success) { + assembly { + let ptr := mload(0x40) + let size := returndatasize() + returndatacopy(ptr, 0, size) + revert(ptr, size) + } + } + } + } + + function executeTransaction( + bytes calldata data, + address destination, + uint256 amount + ) external payable onlyOwner { + if (data.length > 0) require(destination.isContract(), "NO_CODE_AT_ADDR"); + (bool success, ) = destination.call{ value: amount }(data); + if (!success) { + assembly { + let ptr := mload(0x40) + let size := returndatasize() + returndatacopy(ptr, 0, size) + revert(ptr, size) + } + } + } + + function returnOldDeposits(IRollupUser rollup, address payable[] calldata stakers) + external + onlyOwner + { + uint256 stakerCount = stakers.length; + for (uint256 i = 0; i < stakerCount; i++) { + try rollup.returnOldDeposit(stakers[i]) {} catch (bytes memory error) { + if (error.length == 0) { + // Assume out of gas + // We need to revert here so gas estimation works + require(false, "GAS"); + } + } + } + } + + function timeoutChallenges(IChallenge[] calldata challenges) external onlyOwner { + uint256 challengesCount = challenges.length; + for (uint256 i = 0; i < challengesCount; i++) { + try challenges[i].timeout() {} catch (bytes memory error) { + if (error.length == 0) { + // Assume out of gas + // We need to revert here so gas estimation works + require(false, "GAS"); + } + } + } + } +} diff --git a/solgen/src/rollup/ValidatorWalletCreator.sol b/solgen/src/rollup/ValidatorWalletCreator.sol new file mode 100644 index 0000000000..b6ebf370e8 --- /dev/null +++ b/solgen/src/rollup/ValidatorWalletCreator.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +import "./ValidatorWallet.sol"; + +contract ValidatorWalletCreator is Ownable { + event WalletCreated( + address indexed walletAddress, + address indexed userAddress, + address adminProxy + ); + event TemplateUpdated(); + + address public template; + + constructor() public Ownable() { + template = address(new ValidatorWallet()); + } + + function setTemplate(address _template) external onlyOwner { + template = _template; + emit TemplateUpdated(); + } + + function createWallet() external returns (address) { + ProxyAdmin admin = new ProxyAdmin(); + address proxy = address( + new TransparentUpgradeableProxy(address(template), address(admin), "") + ); + admin.transferOwnership(msg.sender); + ValidatorWallet(proxy).initialize(); + ValidatorWallet(proxy).transferOwnership(msg.sender); + emit WalletCreated(proxy, msg.sender, address(admin)); + return proxy; + } +} diff --git a/validator/builderbackend.go b/validator/builderbackend.go new file mode 100644 index 0000000000..85d38f337c --- /dev/null +++ b/validator/builderbackend.go @@ -0,0 +1,132 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/pkg/errors" +) + +type BuilderBackend struct { + transactions []*types.Transaction + builderAuth *bind.TransactOpts + realSender common.Address + wallet *common.Address + + realClient arbutil.L1Interface +} + +func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { + randKey, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + fakeAuth, err := bind.NewKeyedTransactorWithChainID(randKey, big.NewInt(9999999)) + if err != nil { + return nil, err + } + return &BuilderBackend{ + builderAuth: fakeAuth, + realSender: wallet.From(), + wallet: wallet.Address(), + realClient: wallet.client, + }, nil +} + +func (b *BuilderBackend) TransactionCount() int { + return len(b.transactions) +} + +func (b *BuilderBackend) ClearTransactions() { + b.transactions = nil +} + +func (b *BuilderBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return &types.Header{}, nil +} + +func (b *BuilderBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + +func (b *BuilderBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + return []byte{1}, nil +} + +func (b *BuilderBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + panic("implement me") +} + +func (b *BuilderBackend) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { + return []byte{1}, nil +} + +func (b *BuilderBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + return 0, nil +} + +func (b *BuilderBackend) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return 0, nil +} + +func (b *BuilderBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return big.NewInt(0), nil +} + +func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { + return 0, nil +} + +func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + b.transactions = append(b.transactions, tx) + data, dest, amount, totalAmount := combineTxes(b.transactions) + if b.wallet == nil { + return nil + } + realData, err := validatorABI.Pack("executeTransactions", data, dest, amount) + if err != nil { + return err + } + msg := ethereum.CallMsg{ + From: b.realSender, + To: b.wallet, + Value: totalAmount, + Data: realData, + } + _, err = b.realClient.EstimateGas(ctx, msg) + return errors.WithStack(err) +} + +func (b *BuilderBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { + panic("implement me") +} + +func (b *BuilderBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { + panic("implement me") +} + +func authWithContextAndAmount(ctx context.Context, auth *bind.TransactOpts, amount *big.Int) *bind.TransactOpts { + return &bind.TransactOpts{ + From: auth.From, + Nonce: auth.Nonce, + Signer: auth.Signer, + Value: amount, + GasPrice: auth.GasPrice, + GasLimit: auth.GasLimit, + Context: ctx, + } +} + +func authWithContext(ctx context.Context, auth *bind.TransactOpts) *bind.TransactOpts { + return authWithContextAndAmount(ctx, auth, big.NewInt(0)) +} diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 0c8a34a2c8..05c633a4dd 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -185,7 +185,7 @@ type OurStakerInfo struct { *ethbridge.StakerInfo } -func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy Strategy, fromBlock int64) (nodeAction, bool, error) { +func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy, fromBlock int64) (nodeAction, bool, error) { startState, err := lookupNodeStartState(ctx, v.rollup.RollupWatcher, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) if err != nil { return nil, false, err diff --git a/validator/staker.go b/validator/staker.go index f05f4e106c..ac73b13999 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -15,14 +15,15 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbutil" - "github.com/offchainlabs/arbstate/validator" "github.com/pkg/errors" ) -type Strategy uint8 +const txTimeout time.Duration = 5 * time.Minute + +type StakerStrategy uint8 const ( - WatchtowerStrategy Strategy = iota + WatchtowerStrategy StakerStrategy = iota DefensiveStrategy StakeLatestStrategy MakeNodesStrategy @@ -43,7 +44,7 @@ type StakerInfo struct { type Staker struct { *Validator activeChallenge *validator.ChallengeManager - strategy Strategy + strategy StakerStrategy fromBlock int64 baseCallOpts bind.CallOpts auth *bind.TransactOpts @@ -61,7 +62,7 @@ func NewStaker( wallet *ethbridge.ValidatorWallet, fromBlock int64, validatorUtilsAddress common.Address, - strategy Strategy, + strategy StakerStrategy, callOpts bind.CallOpts, auth *bind.TransactOpts, config ValidatorConfig, @@ -98,7 +99,7 @@ func (s *Staker) RunInBackground(ctx context.Context, stakerDelay time.Duration) arbTx, err := s.Act(ctx) if err == nil && arbTx != nil { // Note: methodName isn't accurate, it's just used for logging - _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, s.client, arbTx, 5*time.Minute) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, s.client, arbTx, txTimeout) err = errors.Wrap(err, "error waiting for tx receipt") if err == nil { log.Info("successfully executed staker transaction", "hash", arbTx.Hash()) @@ -356,7 +357,7 @@ func (s *Staker) newStake(ctx context.Context) error { return s.rollup.NewStake(ctx, stakeAmount) } -func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy Strategy) error { +func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { active := effectiveStrategy >= StakeLatestStrategy action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, s.fromBlock) if err != nil { diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go new file mode 100644 index 0000000000..1b7bac9c66 --- /dev/null +++ b/validator/validator_wallet.go @@ -0,0 +1,235 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" + "github.com/pkg/errors" +) + +var validatorABI abi.ABI +var walletCreatedID common.Hash + +func init() { + parsedValidator, err := abi.JSON(strings.NewReader(rollupgen.ValidatorWalletABI)) + if err != nil { + panic(err) + } + validatorABI = parsedValidator + + parsedValidatorWalletCreator, err := abi.JSON(strings.NewReader(rollupgen.ValidatorWalletCreatorABI)) + if err != nil { + panic(err) + } + walletCreatedID = parsedValidatorWalletCreator.Events["WalletCreated"].ID +} + +type ValidatorWallet struct { + con *rollupgen.ValidatorWallet + address *common.Address + onWalletCreated func(common.Address) + client arbutil.L1Interface + auth *bind.TransactOpts + rollupAddress common.Address + walletFactoryAddr common.Address + rollupFromBlock int64 +} + +func NewValidator(address *common.Address, walletFactoryAddr, rollupAddress common.Address, client arbutil.L1Interface, auth *bind.TransactOpts, rollupFromBlock int64, onWalletCreated func(common.Address)) (*ValidatorWallet, error) { + var con *rollupgen.ValidatorWallet + if address != nil { + var err error + con, err = rollupgen.NewValidatorWallet(*address, client) + if err != nil { + return nil, err + } + } + return &ValidatorWallet{ + con: con, + address: address, + onWalletCreated: onWalletCreated, + client: client, + auth: auth, + rollupAddress: rollupAddress, + walletFactoryAddr: walletFactoryAddr, + rollupFromBlock: rollupFromBlock, + }, nil +} + +// May be the nil if the wallet hasn't been deployed yet +func (v *ValidatorWallet) Address() *common.Address { + return v.address +} + +func (v *ValidatorWallet) From() common.Address { + return v.auth.From +} + +func (v *ValidatorWallet) RollupAddress() common.Address { + return v.rollupAddress +} + +func (v *ValidatorWallet) executeTransaction(ctx context.Context, tx *types.Transaction) (*types.Transaction, error) { + oldAuthValue := v.auth.Value + v.auth.Value = tx.Value() + defer (func() { v.auth.Value = oldAuthValue })() + + return v.con.ExecuteTransaction(v.auth, tx.Data(), *tx.To(), tx.Value()) +} + +func (v *ValidatorWallet) createWalletIfNeeded(ctx context.Context) error { + if v.con != nil { + return nil + } + if v.address == nil { + addr, err := CreateValidatorWallet(ctx, v.walletFactoryAddr, v.rollupFromBlock, v.auth, v.client) + if err != nil { + return err + } + v.address = &addr + if v.onWalletCreated != nil { + v.onWalletCreated(addr) + } + } + con, err := rollupgen.NewValidator(*v.address, v.client) + if err != nil { + return err + } + v.con = con + return nil +} + +func combineTxes(txes []*types.Transaction) ([][]byte, []common.Address, []*big.Int, *big.Int) { + totalAmount := big.NewInt(0) + data := make([][]byte, 0, len(txes)) + dest := make([]common.Address, 0, len(txes)) + amount := make([]*big.Int, 0, len(txes)) + + for _, tx := range txes { + data = append(data, tx.Data()) + dest = append(dest, *tx.To()) + amount = append(amount, tx.Value()) + totalAmount = totalAmount.Add(totalAmount, tx.Value()) + } + return data, dest, amount, totalAmount +} + +// Not thread safe! Don't call this from multiple threads at the same time. +func (v *ValidatorWallet) ExecuteTransactions(ctx context.Context, builder *BuilderBackend) (*types.Transaction, error) { + txes := builder.transactions + if len(txes) == 0 { + return nil, nil + } + + if len(txes) == 1 { + arbTx, err := v.executeTransaction(ctx, txes[0]) + if err != nil { + return nil, err + } + builder.transactions = nil + return arbTx, nil + } + + totalAmount := big.NewInt(0) + data := make([][]byte, 0, len(txes)) + dest := make([]common.Address, 0, len(txes)) + amount := make([]*big.Int, 0, len(txes)) + + for _, tx := range txes { + data = append(data, tx.Data()) + dest = append(dest, *tx.To()) + amount = append(amount, tx.Value()) + totalAmount = totalAmount.Add(totalAmount, tx.Value()) + } + + err := v.createWalletIfNeeded(ctx) + if err != nil { + return nil, err + } + + oldAuthValue := v.auth.Value + v.auth.Value = totalAmount + defer (func() { v.auth.Value = oldAuthValue })() + + arbTx, err := v.con.ExecuteTransactions(v.auth, data, dest, amount) + if err != nil { + return nil, err + } + builder.transactions = nil + return arbTx, nil +} + +func (v *ValidatorWallet) ReturnOldDeposits(ctx context.Context, stakers []common.Address) (*types.Transaction, error) { + return v.con.ReturnOldDeposits(v.auth, v.rollupAddress, stakers) +} + +func (v *ValidatorWallet) TimeoutChallenges(ctx context.Context, challenges []common.Address) (*types.Transaction, error) { + return v.con.TimeoutChallenges(v.auth, challenges) +} + +func CreateValidatorWallet( + ctx context.Context, + validatorWalletFactoryAddr common.Address, + fromBlock int64, + transactAuth *bind.TransactOpts, + client arbutil.L1Interface, +) (common.Address, error) { + walletCreator, err := rollupgen.NewValidatorWalletCreator(validatorWalletFactoryAddr, client) + if err != nil { + return common.Address{}, errors.WithStack(err) + } + + query := ethereum.FilterQuery{ + BlockHash: nil, + FromBlock: big.NewInt(fromBlock), + ToBlock: nil, + Addresses: []common.Address{validatorWalletFactoryAddr}, + Topics: [][]common.Hash{{walletCreatedID}, nil, {transactAuth.From.Hash()}}, + } + logs, err := client.FilterLogs(ctx, query) + if err != nil { + return common.Address{}, errors.WithStack(err) + } + if len(logs) > 1 { + return common.Address{}, errors.New("more than one validator wallet created for address") + } else if len(logs) == 1 { + log := logs[0] + parsed, err := walletCreator.ParseWalletCreated(log) + if err != nil { + return common.Address{}, err + } + return parsed.WalletAddress, err + } + + tx, err := walletCreator.CreateWallet(transactAuth) + if err != nil { + return common.Address{}, err + } + + receipt, err := arbutil.EnsureTxSucceededWithTimeout( + ctx, + client, + tx, + txTimeout, + ) + if err != nil { + return common.Address{}, err + } + ev, err := walletCreator.ParseWalletCreated(*receipt.Logs[len(receipt.Logs)-1]) + if err != nil { + return common.Address{}, errors.WithStack(err) + } + return ev.WalletAddress, nil +} From a4acfea625ed9d9fba242702c4612d0ac72d6109 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 2 Feb 2022 14:31:38 -0600 Subject: [PATCH 003/110] Compiling! --- arbnode/delayed_sequencer.go | 2 +- arbnode/sequencer.go | 2 +- arbnode/transaction_streamer.go | 5 + solgen/src/rollup/IRollupCore.sol | 6 +- solgen/src/rollup/IRollupLogic.sol | 3 +- solgen/src/rollup/RollupAdminLogic.sol | 9 +- solgen/src/rollup/RollupCore.sol | 6 +- solgen/src/rollup/RollupLib.sol | 30 +- solgen/src/rollup/RollupUserLogic.sol | 67 ++-- solgen/src/rollup/ValidatorUtils.sol | 2 +- validator/assertion.go | 165 +++++++++ validator/block_validator.go | 2 + validator/builder_backend.go | 88 +++++ validator/builderbackend.go | 132 ------- validator/challenge_manager.go | 19 +- validator/l1_validator.go | 485 ++++++++++++------------- validator/rollup_watcher.go | 217 +++++++++++ validator/staker.go | 140 ++++--- validator/validator_utils.go | 162 +++++++++ validator/validator_wallet.go | 4 +- 20 files changed, 1040 insertions(+), 506 deletions(-) create mode 100644 validator/assertion.go create mode 100644 validator/builder_backend.go delete mode 100644 validator/builderbackend.go create mode 100644 validator/rollup_watcher.go create mode 100644 validator/validator_utils.go diff --git a/arbnode/delayed_sequencer.go b/arbnode/delayed_sequencer.go index c6770f7c88..92571f94e5 100644 --- a/arbnode/delayed_sequencer.go +++ b/arbnode/delayed_sequencer.go @@ -137,7 +137,7 @@ func (d *DelayedSequencer) update(ctx context.Context) error { } func (d *DelayedSequencer) run(ctx context.Context) error { - headerChan, cancel := HeaderSubscribeWithRetry(ctx, d.client) + headerChan, cancel := arbutil.HeaderSubscribeWithRetry(ctx, d.client) defer cancel() for { diff --git a/arbnode/sequencer.go b/arbnode/sequencer.go index 1bdb9921db..902d9a9c80 100644 --- a/arbnode/sequencer.go +++ b/arbnode/sequencer.go @@ -83,7 +83,7 @@ func (s *Sequencer) Start(ctx context.Context) error { return errors.New("ArbInterface: not initialized") } - headerChan, cancel := HeaderSubscribeWithRetry(ctx, s.l1Client) + headerChan, cancel := arbutil.HeaderSubscribeWithRetry(ctx, s.l1Client) defer cancel() go (func() { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 78bbe726c2..c48c22891b 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -340,6 +340,11 @@ func (s *TransactionStreamer) SequenceDelayedMessages(messages []*arbos.L1Incomi return s.writeMessages(pos, messagesWithMeta, nil) } +func (s *TransactionStreamer) GetGenesisBlockNumber() (uint64, error) { + // TODO: when block 0 is no longer necessarily the genesis, track this + return 0, nil +} + // The mutex must be held, and pos must be the latest message count. // `batch` may be nil, which initializes a new batch. The batch is closed out in this function. func (s *TransactionStreamer) writeMessages(pos uint64, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { diff --git a/solgen/src/rollup/IRollupCore.sol b/solgen/src/rollup/IRollupCore.sol index c7db04a228..f180177f65 100644 --- a/solgen/src/rollup/IRollupCore.sol +++ b/solgen/src/rollup/IRollupCore.sol @@ -21,6 +21,7 @@ pragma solidity ^0.8.0; import "./Node.sol"; import "../challenge/IChallenge.sol"; +import "../osp/IOneStepProofEntry.sol"; interface IRollupCore { function _stakerMap(address stakerAddress) @@ -37,14 +38,15 @@ interface IRollupCore { event RollupCreated(bytes32 machineHash); event NodeCreated( - uint256 indexed nodeNum, + uint64 indexed nodeNum, bytes32 indexed parentNodeHash, bytes32 nodeHash, bytes32 executionHash, uint256 inboxMaxCount, bytes32 afterInboxBatchAcc, bytes32[2][2] assertionBytes32Fields, - uint64[2][2] assertionIntFields, + uint64[3][2] assertionIntFields, + uint64 numBlocks, bytes32 wasmModuleRoot ); diff --git a/solgen/src/rollup/IRollupLogic.sol b/solgen/src/rollup/IRollupLogic.sol index 4a91febcfe..20c20bf85b 100644 --- a/solgen/src/rollup/IRollupLogic.sol +++ b/solgen/src/rollup/IRollupLogic.sol @@ -166,11 +166,10 @@ interface IRollupAdmin { function forceCreateNode( bytes32 expectedNodeHash, bytes32[2][2] calldata assertionBytes32Fields, - uint64[2][2] calldata assertionIntFields, + uint64[3][2] calldata assertionIntFields, uint256 beforeInboxMaxCount, uint256 inboxMaxCount, uint64 numBlocks, - bool errored, uint64 prevNode ) external; diff --git a/solgen/src/rollup/RollupAdminLogic.sol b/solgen/src/rollup/RollupAdminLogic.sol index 20b3e8b6c5..8da9dafd1c 100644 --- a/solgen/src/rollup/RollupAdminLogic.sol +++ b/solgen/src/rollup/RollupAdminLogic.sol @@ -238,11 +238,10 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin { function forceCreateNode( bytes32 expectedNodeHash, bytes32[2][2] calldata assertionBytes32Fields, - uint64[2][2] calldata assertionIntFields, + uint64[3][2] calldata assertionIntFields, uint256 beforeInboxMaxCount, uint256 inboxMaxCount, uint64 numBlocks, - bool errored, uint64 prevNode ) external override whenPaused { require(prevNode == latestConfirmed(), "ONLY_LATEST_CONFIRMED"); @@ -252,8 +251,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin { assertionIntFields, beforeInboxMaxCount, inboxMaxCount, - numBlocks, - errored + numBlocks ); createNewNode( @@ -261,7 +259,8 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin { assertionBytes32Fields, assertionIntFields, prevNode, - expectedNodeHash + expectedNodeHash, + numBlocks ); emit OwnerFunctionCalled(23); diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 9266eb9749..a758e622d6 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -591,9 +591,10 @@ abstract contract RollupCore is IRollupCore, Cloneable, Pausable { function createNewNode( RollupLib.Assertion memory assertion, bytes32[2][2] calldata assertionBytes32Fields, - uint64[2][2] calldata assertionIntFields, + uint64[3][2] calldata assertionIntFields, uint64 prevNodeNum, - bytes32 expectedNodeHash + bytes32 expectedNodeHash, + uint64 numBlocks ) internal returns (bytes32 newNodeHash) { StakeOnNewNodeFrame memory memoryFrame; { @@ -688,6 +689,7 @@ abstract contract RollupCore is IRollupCore, Cloneable, Pausable { memoryFrame.sequencerBatchAcc, assertionBytes32Fields, assertionIntFields, + numBlocks, wasmModuleRoot ); diff --git a/solgen/src/rollup/RollupLib.sol b/solgen/src/rollup/RollupLib.sol index 7a6eef144d..0b10487543 100644 --- a/solgen/src/rollup/RollupLib.sol +++ b/solgen/src/rollup/RollupLib.sol @@ -62,19 +62,17 @@ library RollupLib { function decodeExecutionState( bytes32[2] memory bytes32Fields, - uint64[2] memory intFields, - bool errored, + uint64[3] memory intFields, uint256 inboxMaxCount ) internal pure returns (ExecutionState memory) { - MachineStatus machineStatus; - if (errored) { - machineStatus = MachineStatus.ERRORED; - } else { - machineStatus = MachineStatus.FINISHED; - } + require(intFields[2] == uint64(MachineStatus.FINISHED) || intFields[2] == uint64(MachineStatus.ERRORED), "BAD_STATUS"); + MachineStatus machineStatus = MachineStatus(intFields[2]); + uint64[2] memory gsIntFields; + gsIntFields[0] = intFields[0]; + gsIntFields[1] = intFields[1]; return ExecutionState( - GlobalState(bytes32Fields, intFields), + GlobalState(bytes32Fields, gsIntFields), inboxMaxCount, machineStatus ); @@ -82,24 +80,21 @@ library RollupLib { function decodeAssertion( bytes32[2][2] memory bytes32Fields, - uint64[2][2] memory intFields, + uint64[3][2] memory intFields, uint256 beforeInboxMaxCount, uint256 inboxMaxCount, - uint64 numBlocks, - bool errored + uint64 numBlocks ) internal pure returns (Assertion memory) { return Assertion( decodeExecutionState( bytes32Fields[0], intFields[0], - false, beforeInboxMaxCount ), decodeExecutionState( - bytes32Fields[0], - intFields[0], - errored, + bytes32Fields[1], + intFields[1], inboxMaxCount ), numBlocks @@ -148,7 +143,6 @@ library RollupLib { challengeRootHash( assertionExecHash, blockProposed, - GlobalStates.getInboxPosition(assertion.afterState.globalState), wasmModuleRoot ); } @@ -156,7 +150,6 @@ library RollupLib { function challengeRootHash( bytes32 execution, uint256 proposedTime, - uint256 maxMessageCount, bytes32 wasmModuleRoot ) internal pure returns (bytes32) { return @@ -164,7 +157,6 @@ library RollupLib { abi.encodePacked( execution, proposedTime, - maxMessageCount, wasmModuleRoot ) ); diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 142ad2121b..7793fdc54a 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -5,7 +5,11 @@ pragma solidity ^0.8.0; import "./Rollup.sol"; import "./IRollupLogic.sol"; -abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResultReceiver { +abstract contract AbsRollupUserLogic is + RollupCore, + IRollupUser, + IChallengeResultReceiver +{ using NodeLib for Node; function initialize(address _stakeToken) public virtual override; @@ -161,16 +165,13 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul * @param assertionIntFields Assertion data for creating * @param beforeInboxMaxCount Inbox count at previous assertion * @param numBlocks The number of blocks since the last node - * @param errored If the machine enters the errored state during this assertion */ function stakeOnNewNode( bytes32 expectedNodeHash, bytes32[2][2] calldata assertionBytes32Fields, - uint64[2][2] calldata assertionIntFields, + uint64[3][2] calldata assertionIntFields, uint256 beforeInboxMaxCount, - uint256 inboxMaxCount, - uint64 numBlocks, - bool errored + uint64 numBlocks ) external onlyValidator whenNotPaused { require(isStaked(msg.sender), "NOT_STAKED"); // Ensure staker is staked on the previous node @@ -180,9 +181,8 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul assertionBytes32Fields, assertionIntFields, beforeInboxMaxCount, - inboxMaxCount, - numBlocks, - errored + sequencerBridge.batchCount(), + numBlocks ); { @@ -199,6 +199,16 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul ) >= assertion.beforeState.inboxMaxCount, "TOO_SMALL" ); + // Minimum size requirement: any assertion must advance the inbox at least one batch + require( + GlobalStates.getInboxPosition( + assertion.afterState.globalState + ) > + GlobalStates.getInboxPosition( + assertion.beforeState.globalState + ), + "SAME_BATCH" + ); // The rollup cannot advance normally from an errored state require( @@ -211,7 +221,8 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul assertionBytes32Fields, assertionIntFields, prevNode, - expectedNodeHash + expectedNodeHash, + numBlocks ); stakeOnNode(msg.sender, latestNodeCreated()); @@ -277,7 +288,6 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul * @param globalStates The before and after global state, per assertion * @param numBlocks The number of L2 blocks contained in each assertion * @param proposedTimes Times that the two nodes were proposed - * @param maxMessageCounts Total number of messages consumed by the two nodes */ function createChallenge( address[2] calldata stakers, @@ -286,7 +296,6 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul GlobalState[2][2] calldata globalStates, uint64[2] calldata numBlocks, uint256[2] calldata proposedTimes, - uint256[2] calldata maxMessageCounts, bytes32[2] calldata wasmModuleRoots ) external onlyValidator whenNotPaused { require(nodeNums[0] < nodeNums[1], "WRONG_ORDER"); @@ -316,7 +325,6 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul numBlocks[0] ), proposedTimes[0], - maxMessageCounts[0], wasmModuleRoots[0] ), "CHAL_HASH1" @@ -331,7 +339,6 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul numBlocks[1] ), proposedTimes[1], - maxMessageCounts[1], wasmModuleRoots[1] ), "CHAL_HASH2" @@ -377,17 +384,22 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul uint256 asserterTimeLeft, uint256 challengerTimeLeft ) internal returns (IChallenge) { - return challengeFactory.createChallenge( - [address(this), address(sequencerBridge), address(delayedBridge)], - wasmModuleRoots[0], - machineStatuses[0], - globalStates[0], - numBlocks[0], - stakers[0], - stakers[1], - asserterTimeLeft, - challengerTimeLeft - ); + return + challengeFactory.createChallenge( + [ + address(this), + address(sequencerBridge), + address(delayedBridge) + ], + wasmModuleRoots[0], + machineStatuses[0], + globalStates[0], + numBlocks[0], + stakers[0], + stakers[1], + asserterTimeLeft, + challengerTimeLeft + ); } /** @@ -628,7 +640,10 @@ abstract contract AbsRollupUserLogic is RollupCore, IRollupUser, IChallengeResul */ function requireUnchallengedStaker(address stakerAddress) private view { require(isStaked(stakerAddress), "NOT_STAKED"); - require(address(currentChallenge(stakerAddress)) == address(0), "IN_CHAL"); + require( + address(currentChallenge(stakerAddress)) == address(0), + "IN_CHAL" + ); } function withdrawStakerFunds(address payable destination) diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index 3531114c71..c039307054 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -44,7 +44,7 @@ contract ValidatorUtils { view returns ( bool isStaked, - uint256 latestStakedNode, + uint64 latestStakedNode, uint256 amountStaked, IChallenge currentChallenge ) diff --git a/validator/assertion.go b/validator/assertion.go new file mode 100644 index 0000000000..dadc6ce4a3 --- /dev/null +++ b/validator/assertion.go @@ -0,0 +1,165 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" +) + +type MachineStatus uint8 + +const ( + MachineStatusRunning MachineStatus = 0 + MachineStatusFinished MachineStatus = 1 + MachineStatusErrored MachineStatus = 2 + MachineStatusTooFar MachineStatus = 3 +) + +type ExecutionState struct { + GlobalState GoGlobalState + MachineStatus MachineStatus +} + +func newExecutionStateFromFields(a [2][32]byte, b [3]uint64) *ExecutionState { + if b[2] >= (1 << 8) { + panic(fmt.Sprintf("invalid machine status %v", b[2])) + } + return &ExecutionState{ + GlobalState: GoGlobalState{ + BlockHash: a[0], + SendRoot: a[1], + Batch: b[0], + PosInBatch: b[1], + }, + MachineStatus: MachineStatus(uint8(b[2])), + } +} + +func NewAssertionFromFields(a [2][2][32]byte, b [2][3]uint64) *Assertion { + return &Assertion{ + BeforeState: newExecutionStateFromFields(a[0], b[0]), + AfterState: newExecutionStateFromFields(a[1], b[1]), + } +} + +func (s *ExecutionState) ByteFields() [2][32]byte { + return [2][32]byte{ + s.GlobalState.BlockHash, + s.GlobalState.SendRoot, + } +} + +func (s *ExecutionState) IntFields() [3]uint64 { + return [3]uint64{ + s.GlobalState.Batch, + s.GlobalState.PosInBatch, + uint64(s.MachineStatus), + } +} + +func (a *ExecutionState) BlockStateHash() common.Hash { + if a.MachineStatus == MachineStatusFinished { + return crypto.Keccak256Hash([]byte("Block state:"), a.GlobalState.Hash().Bytes()) + } else if a.MachineStatus == MachineStatusErrored { + return crypto.Keccak256Hash([]byte("Block state, errored:"), a.GlobalState.Hash().Bytes()) + } else if a.MachineStatus == MachineStatusTooFar { + return crypto.Keccak256Hash([]byte("Block state, too far:")) + } else { + panic(fmt.Sprintf("invalid machine status %v", a.MachineStatus)) + } +} + +func (a *Assertion) BytesFields() [2][2][32]byte { + return [2][2][32]byte{ + a.BeforeState.ByteFields(), + a.AfterState.ByteFields(), + } +} + +func (a *Assertion) IntFields() [2][3]uint64 { + return [2][3]uint64{ + a.BeforeState.IntFields(), + a.AfterState.IntFields(), + } +} + +func (a *Assertion) BeforeExecutionHash() common.Hash { + return a.BeforeState.BlockStateHash() +} + +func (a *Assertion) AfterExecutionHash() common.Hash { + return a.AfterState.BlockStateHash() +} + +func BisectionChunkHash( + segmentStart uint64, + segmentLength uint64, + startHash common.Hash, + endHash common.Hash, +) common.Hash { + return crypto.Keccak256Hash( + math.U256Bytes(new(big.Int).SetUint64(segmentStart)), + math.U256Bytes(new(big.Int).SetUint64(segmentLength)), + startHash[:], + endHash[:], + ) +} + +func (a *Assertion) ExecutionHash() common.Hash { + return BisectionChunkHash( + 0, + a.NumBlocks, + a.BeforeExecutionHash(), + a.AfterExecutionHash(), + ) +} + +type NodeState struct { + InboxMaxCount *big.Int + *ExecutionState +} + +type Assertion struct { + BeforeState *ExecutionState + AfterState *ExecutionState + NumBlocks uint64 +} + +type NodeInfo struct { + NodeNum uint64 + BlockProposed uint64 + Assertion *Assertion + InboxMaxCount *big.Int + AfterInboxBatchAcc common.Hash + NodeHash common.Hash + WasmModuleRoot common.Hash +} + +func (n *NodeInfo) AfterState() *NodeState { + return &NodeState{ + InboxMaxCount: n.InboxMaxCount, + ExecutionState: n.Assertion.AfterState, + } +} + +func (n *NodeInfo) MachineStatuses() [2]uint8 { + return [2]uint8{ + uint8(n.Assertion.BeforeState.MachineStatus), + uint8(n.Assertion.AfterState.MachineStatus), + } +} + +func (n *NodeInfo) GlobalStates() [2]rollupgen.GlobalState { + return [2]rollupgen.GlobalState{ + rollupgen.GlobalState(n.Assertion.BeforeState.GlobalState.AsSolidityStruct()), + rollupgen.GlobalState(n.Assertion.AfterState.GlobalState.AsSolidityStruct()), + } +} diff --git a/validator/block_validator.go b/validator/block_validator.go index d61557995f..90b7834d0b 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -68,12 +68,14 @@ type InboxTrackerInterface interface { BlockValidatorRegistrer GetDelayedMessageBytes(uint64) ([]byte, error) GetBatchMessageCount(seqNum uint64) (uint64, error) + GetBatchAcc(seqNum uint64) (common.Hash, error) GetBatchCount() (uint64, error) } type TransactionStreamerInterface interface { BlockValidatorRegistrer GetMessage(seqNum uint64) (arbstate.MessageWithMetadata, error) + GetGenesisBlockNumber() (uint64, error) } type InboxReaderInterface interface { diff --git a/validator/builder_backend.go b/validator/builder_backend.go new file mode 100644 index 0000000000..44178bb8f4 --- /dev/null +++ b/validator/builder_backend.go @@ -0,0 +1,88 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/pkg/errors" +) + +type BuilderBackend struct { + transactions []*types.Transaction + builderAuth *bind.TransactOpts + realSender common.Address + wallet *common.Address + + arbutil.L1Interface +} + +func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { + randKey, err := crypto.GenerateKey() + if err != nil { + return nil, err + } + fakeAuth, err := bind.NewKeyedTransactorWithChainID(randKey, big.NewInt(9999999)) + if err != nil { + return nil, err + } + return &BuilderBackend{ + builderAuth: fakeAuth, + realSender: wallet.From(), + wallet: wallet.Address(), + L1Interface: wallet.client, + }, nil +} + +func (b *BuilderBackend) BuilderTransactionCount() int { + return len(b.transactions) +} + +func (b *BuilderBackend) ClearTransactions() { + b.transactions = nil +} + +func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + b.transactions = append(b.transactions, tx) + data, dest, amount, totalAmount := combineTxes(b.transactions) + if b.wallet == nil { + return nil + } + realData, err := validatorABI.Pack("executeTransactions", data, dest, amount) + if err != nil { + return err + } + msg := ethereum.CallMsg{ + From: b.realSender, + To: b.wallet, + Value: totalAmount, + Data: realData, + } + _, err = b.EstimateGas(ctx, msg) + return errors.WithStack(err) +} + +func (b *BuilderBackend) AuthWithAmount(ctx context.Context, amount *big.Int) *bind.TransactOpts { + return &bind.TransactOpts{ + From: b.builderAuth.From, + Nonce: b.builderAuth.Nonce, + Signer: b.builderAuth.Signer, + Value: amount, + GasPrice: b.builderAuth.GasPrice, + GasLimit: b.builderAuth.GasLimit, + Context: ctx, + } +} + +func (b *BuilderBackend) Auth(ctx context.Context) *bind.TransactOpts { + return b.AuthWithAmount(ctx, big.NewInt(0)) +} diff --git a/validator/builderbackend.go b/validator/builderbackend.go deleted file mode 100644 index 85d38f337c..0000000000 --- a/validator/builderbackend.go +++ /dev/null @@ -1,132 +0,0 @@ -// -// Copyright 2021, Offchain Labs, Inc. All rights reserved. -// - -package validator - -import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/offchainlabs/arbstate/arbutil" - "github.com/pkg/errors" -) - -type BuilderBackend struct { - transactions []*types.Transaction - builderAuth *bind.TransactOpts - realSender common.Address - wallet *common.Address - - realClient arbutil.L1Interface -} - -func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { - randKey, err := crypto.GenerateKey() - if err != nil { - return nil, err - } - fakeAuth, err := bind.NewKeyedTransactorWithChainID(randKey, big.NewInt(9999999)) - if err != nil { - return nil, err - } - return &BuilderBackend{ - builderAuth: fakeAuth, - realSender: wallet.From(), - wallet: wallet.Address(), - realClient: wallet.client, - }, nil -} - -func (b *BuilderBackend) TransactionCount() int { - return len(b.transactions) -} - -func (b *BuilderBackend) ClearTransactions() { - b.transactions = nil -} - -func (b *BuilderBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { - return &types.Header{}, nil -} - -func (b *BuilderBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { - return big.NewInt(0), nil -} - -func (b *BuilderBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { - return []byte{1}, nil -} - -func (b *BuilderBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { - panic("implement me") -} - -func (b *BuilderBackend) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { - return []byte{1}, nil -} - -func (b *BuilderBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - return 0, nil -} - -func (b *BuilderBackend) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { - return 0, nil -} - -func (b *BuilderBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return big.NewInt(0), nil -} - -func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { - return 0, nil -} - -func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - b.transactions = append(b.transactions, tx) - data, dest, amount, totalAmount := combineTxes(b.transactions) - if b.wallet == nil { - return nil - } - realData, err := validatorABI.Pack("executeTransactions", data, dest, amount) - if err != nil { - return err - } - msg := ethereum.CallMsg{ - From: b.realSender, - To: b.wallet, - Value: totalAmount, - Data: realData, - } - _, err = b.realClient.EstimateGas(ctx, msg) - return errors.WithStack(err) -} - -func (b *BuilderBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) { - panic("implement me") -} - -func (b *BuilderBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) { - panic("implement me") -} - -func authWithContextAndAmount(ctx context.Context, auth *bind.TransactOpts, amount *big.Int) *bind.TransactOpts { - return &bind.TransactOpts{ - From: auth.From, - Nonce: auth.Nonce, - Signer: auth.Signer, - Value: amount, - GasPrice: auth.GasPrice, - GasLimit: auth.GasLimit, - Context: ctx, - } -} - -func authWithContext(ctx context.Context, auth *bind.TransactOpts) *bind.TransactOpts { - return authWithContextAndAmount(ctx, auth, big.NewInt(0)) -} diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 03893e20b2..a65cfd0d80 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -58,12 +58,13 @@ type ChallengeBackend interface { type ChallengeManager struct { // fields used in both block and execution challenge - con *challengegen.ChallengeCore - challengeAddr common.Address - client bind.ContractBackend - auth *bind.TransactOpts - actingAs common.Address - startL1Block *big.Int + con *challengegen.ChallengeCore + challengeAddr common.Address + rootChallengeAddr common.Address + client bind.ContractBackend + auth *bind.TransactOpts + actingAs common.Address + startL1Block *big.Int // fields below are used while working on block challenge blockChallengeBackend *BlockChallengeBackend @@ -94,6 +95,7 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut return &ChallengeManager{ con: challengeCoreCon, challengeAddr: blockChallengeAddr, + rootChallengeAddr: blockChallengeAddr, client: l1client, auth: auth, actingAs: auth.From, @@ -120,6 +122,7 @@ func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBac return &ChallengeManager{ con: challengeCoreCon, challengeAddr: execChallengeAddr, + rootChallengeAddr: execChallengeAddr, client: l1client, auth: auth, actingAs: auth.From, @@ -140,6 +143,10 @@ type ChallengeState struct { RawSegments [][32]byte } +func (m *ChallengeManager) RootChallengeAddress() common.Address { + return m.rootChallengeAddr +} + // Given the challenge's state hash, resolve the full challenge state via the Bisected event. func (m *ChallengeManager) resolveStateHash(ctx context.Context, stateHash common.Hash) (ChallengeState, error) { logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 05c633a4dd..7ec0d0cffc 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -6,89 +6,123 @@ package validator import ( "context" - "encoding/hex" + "fmt" "math/big" "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common/math" - "github.com/offchainlabs/arbitrum/packages/arb-util/arbtransaction" - "github.com/offchainlabs/arbitrum/packages/arb-util/common" - "github.com/offchainlabs/arbitrum/packages/arb-util/core" - "github.com/offchainlabs/arbitrum/packages/arb-util/ethutils" - "github.com/offchainlabs/arbitrum/packages/arb-util/hashing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/bridgegen" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/pkg/errors" ) type Validator struct { - rollup *ethbridge.Rollup - sequencerInbox *ethbridge.SequencerInboxWatcher - validatorUtils *ethbridge.ValidatorUtils - client ethutils.EthClient - builder *ethbridge.BuilderBackend - wallet *ethbridge.ValidatorWallet - GasThreshold *big.Int - SendThreshold *big.Int - BlockThreshold *big.Int + rollup *RollupWatcher + rollupAddress common.Address + sequencerInbox *bridgegen.SequencerInbox + validatorUtils *rollupgen.ValidatorUtils + client arbutil.L1Interface + builder *BuilderBackend + wallet *ValidatorWallet + callOpts bind.CallOpts + GasThreshold *big.Int + SendThreshold *big.Int + BlockThreshold *big.Int + genesisBlockNumber uint64 + + l2Blockchain *core.BlockChain + inboxReader InboxReaderInterface + inboxTracker InboxTrackerInterface + txStreamer TransactionStreamerInterface + blockValidator *BlockValidator } func NewValidator( ctx context.Context, - client ethutils.EthClient, - wallet *ethbridge.ValidatorWallet, + client arbutil.L1Interface, + wallet *ValidatorWallet, fromBlock int64, validatorUtilsAddress common.Address, callOpts bind.CallOpts, + l2Blockchain *core.BlockChain, + inboxReader InboxReaderInterface, + inboxTracker InboxTrackerInterface, + txStreamer TransactionStreamerInterface, + blockValidator *BlockValidator, ) (*Validator, error) { - builder, err := ethbridge.NewBuilderBackend(wallet) + builder, err := NewBuilderBackend(wallet) if err != nil { return nil, err } - rollup, err := ethbridge.NewRollup(wallet.RollupAddress().ToEthAddress(), fromBlock, client, builder, callOpts) + rollup, err := NewRollupWatcher(wallet.RollupAddress(), fromBlock, builder, callOpts) _ = rollup if err != nil { return nil, err } - sequencerBridgeAddress, err := rollup.SequencerBridge(ctx) + localCallOpts := callOpts + localCallOpts.Context = ctx + sequencerBridgeAddress, err := rollup.SequencerBridge(&localCallOpts) if err != nil { return nil, err } - sequencerInbox, err := ethbridge.NewSequencerInboxWatcher(sequencerBridgeAddress.ToEthAddress(), client) + sequencerInbox, err := bridgegen.NewSequencerInbox(sequencerBridgeAddress, client) if err != nil { return nil, err } - validatorUtils, err := ethbridge.NewValidatorUtils( - validatorUtilsAddress.ToEthAddress(), - wallet.RollupAddress().ToEthAddress(), + validatorUtils, err := rollupgen.NewValidatorUtils( + validatorUtilsAddress, client, - callOpts, ) if err != nil { return nil, err } + genesisBlockNumber, err := txStreamer.GetGenesisBlockNumber() + if err != nil { + return nil, err + } return &Validator{ - rollup: rollup, - sequencerInbox: sequencerInbox, - validatorUtils: validatorUtils, - client: client, - builder: builder, - wallet: wallet, - GasThreshold: big.NewInt(100_000_000_000), - SendThreshold: big.NewInt(5), - BlockThreshold: big.NewInt(960), + rollup: rollup, + rollupAddress: wallet.RollupAddress(), + sequencerInbox: sequencerInbox, + validatorUtils: validatorUtils, + client: client, + builder: builder, + wallet: wallet, + GasThreshold: big.NewInt(100_000_000_000), + SendThreshold: big.NewInt(5), + BlockThreshold: big.NewInt(960), + callOpts: callOpts, + genesisBlockNumber: genesisBlockNumber, + inboxReader: inboxReader, + inboxTracker: inboxTracker, + txStreamer: txStreamer, + blockValidator: blockValidator, }, nil } +func (v *Validator) getCallOpts(ctx context.Context) *bind.CallOpts { + opts := v.callOpts + opts.Context = ctx + return &opts +} + // removeOldStakers removes the stakes of all currently staked validators except // its own if dontRemoveSelf is true -func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) (*arbtransaction.ArbTransaction, error) { - stakersToEliminate, err := v.validatorUtils.RefundableStakers(ctx) +func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) (*types.Transaction, error) { + stakersToEliminate, err := v.validatorUtils.RefundableStakers(v.getCallOpts(ctx), v.rollupAddress) if err != nil { return nil, err } walletAddr := v.wallet.Address() if dontRemoveSelf && walletAddr != nil { for i, staker := range stakersToEliminate { - if staker.ToEthAddress() == *walletAddr { + if staker == *walletAddr { stakersToEliminate[i] = stakersToEliminate[len(stakersToEliminate)-1] stakersToEliminate = stakersToEliminate[:len(stakersToEliminate)-1] break @@ -99,63 +133,62 @@ func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) ( if len(stakersToEliminate) == 0 { return nil, nil } - logger.Info().Int("count", len(stakersToEliminate)).Msg("Removing old stakers") + log.Info("removing old stakers", "count", len(stakersToEliminate)) return v.wallet.ReturnOldDeposits(ctx, stakersToEliminate) } -func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*arbtransaction.ArbTransaction, error) { - challengesToEliminate, err := v.validatorUtils.TimedOutChallenges(ctx, 10) +func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*types.Transaction, error) { + challengesToEliminate, _, err := v.validatorUtils.TimedOutChallenges(v.getCallOpts(ctx), v.rollupAddress, 0, 10) if err != nil { return nil, err } if len(challengesToEliminate) == 0 { return nil, nil } - logger.Info().Int("count", len(challengesToEliminate)).Msg("Timing out challenges") + log.Info("timing out challenges", "count", len(challengesToEliminate)) return v.wallet.TimeoutChallenges(ctx, challengesToEliminate) } -func (v *Validator) resolveNextNode(ctx context.Context, info *ethbridge.StakerInfo, fromBlock int64) error { - confirmType, err := v.validatorUtils.CheckDecidableNextNode(ctx) +func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo, fromBlock int64) error { + callOpts := v.getCallOpts(ctx) + confirmType, err := v.validatorUtils.CheckDecidableNextNode(callOpts, v.rollupAddress) if err != nil { return err } - unresolvedNodeIndex, err := v.rollup.FirstUnresolvedNode(ctx) + unresolvedNodeIndex, err := v.rollup.FirstUnresolvedNode(callOpts) if err != nil { return err } - switch confirmType { - case ethbridge.CONFIRM_TYPE_INVALID: + switch ConfirmType(confirmType) { + case CONFIRM_TYPE_INVALID: addr := v.wallet.Address() - if info == nil || addr == nil || info.LatestStakedNode.Cmp(unresolvedNodeIndex) <= 0 { + if info == nil || addr == nil || info.LatestStakedNode <= unresolvedNodeIndex { // We aren't an example of someone staked on a competitor return nil } - logger.Info().Int("node", int(unresolvedNodeIndex.Int64())).Msg("Rejecting node") - return v.rollup.RejectNextNode(ctx, *addr) - case ethbridge.CONFIRM_TYPE_VALID: - nodeInfo, err := v.rollup.RollupWatcher.LookupNode(ctx, unresolvedNodeIndex) + log.Info("rejecing node", "node", unresolvedNodeIndex) + _, err = v.rollup.RejectNextNode(v.builder.Auth(ctx), *addr) + return err + case CONFIRM_TYPE_VALID: + nodeInfo, err := v.rollup.LookupNode(ctx, unresolvedNodeIndex) if err != nil { return err } - sendCount := new(big.Int).Sub(nodeInfo.Assertion.After.TotalSendCount, nodeInfo.Assertion.Before.TotalSendCount) - sends, err := v.lookup.GetSends(nodeInfo.Assertion.Before.TotalSendCount, sendCount) - if err != nil { - return errors.Wrap(err, "catching up to chain") - } - logger.Info().Int("node", int(unresolvedNodeIndex.Int64())).Msg("Confirming node") - return v.rollup.ConfirmNextNode(ctx, nodeInfo.Assertion, sends) + afterGs := nodeInfo.AfterState().GlobalState + _, err = v.rollup.ConfirmNextNode(v.builder.Auth(ctx), afterGs.BlockHash, afterGs.SendRoot) + return err default: return nil } } func (v *Validator) isRequiredStakeElevated(ctx context.Context) (bool, error) { - requiredStake, err := v.rollup.CurrentRequiredStake(ctx) + callOpts := v.getCallOpts(ctx) + requiredStake, err := v.rollup.CurrentRequiredStake(callOpts) if err != nil { return false, err } - baseStake, err := v.rollup.BaseStake(ctx) + baseStake, err := v.rollup.BaseStake(callOpts) if err != nil { return false, err } @@ -163,174 +196,138 @@ func (v *Validator) isRequiredStakeElevated(ctx context.Context) (bool, error) { } type createNodeAction struct { - assertion *core.Assertion - prevProposedBlock *big.Int - prevInboxMaxCount *big.Int - hash [32]byte - sequencerBatchProof []byte + assertion *Assertion + prevInboxMaxCount *big.Int + hash [32]byte } type existingNodeAction struct { - number *big.Int + number uint64 hash [32]byte } type nodeAction interface{} type OurStakerInfo struct { - LatestStakedNode *big.Int - LatestStakedNodeHash [32]byte - CanProgress bool - latestExecutionCursor core.ExecutionCursor - *ethbridge.StakerInfo + LatestStakedNode uint64 + LatestStakedNodeHash [32]byte + CanProgress bool + *StakerInfo } -func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy, fromBlock int64) (nodeAction, bool, error) { - startState, err := lookupNodeStartState(ctx, v.rollup.RollupWatcher, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) - if err != nil { - return nil, false, err - } - - coreMessageCount := v.lookup.MachineMessagesRead() - if coreMessageCount.Cmp(startState.TotalMessagesRead) < 0 { - logger.Info(). - Str("localcount", coreMessageCount.String()). - Str("target", startState.TotalMessagesRead.String()). - Msg("catching up to chain") - return nil, false, nil +func (v *Validator) blockNumberFromBatchCount(batch uint64) (uint64, error) { + var height uint64 + if batch > 0 { + var err error + height, err = v.inboxTracker.GetBatchMessageCount(batch - 1) + if err != nil { + return 0, err + } } + return height + v.genesisBlockNumber, nil +} - currentBlock, err := getBlockID(ctx, v.client, nil) +func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy, fromBlock int64) (nodeAction, bool, error) { + startState, startStateProposed, err := lookupNodeStartState(ctx, v.rollup, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) if err != nil { return nil, false, err } - minAssertionPeriod, err := v.rollup.MinimumAssertionPeriod(ctx) + localBatchCount, err := v.inboxTracker.GetBatchCount() if err != nil { return nil, false, err } - - timeSinceProposed := new(big.Int).Sub(currentBlock.Height.AsInt(), startState.ProposedBlock) - if timeSinceProposed.Cmp(minAssertionPeriod) < 0 { - // Too soon to assert + if localBatchCount < startState.GlobalState.Batch { + log.Info("catching up to chain batches", "localBatches", localBatchCount, "target", startState.GlobalState.Batch) return nil, false, nil } - cursor := stakerInfo.latestExecutionCursor - if cursor == nil || startState.TotalGasConsumed.Cmp(cursor.TotalGasConsumed()) < 0 { - cursor, err = v.lookup.GetExecutionCursor(startState.TotalGasConsumed, true) + startBlock := v.l2Blockchain.GetBlockByHash(startState.GlobalState.BlockHash) + if startBlock == nil { + expectedBlockHeight, err := v.blockNumberFromBatchCount(startState.GlobalState.Batch) if err != nil { return nil, false, err } - } else { - err = v.lookup.AdvanceExecutionCursor(cursor, new(big.Int).Sub(startState.TotalGasConsumed, cursor.TotalGasConsumed()), false, true) - if err != nil { - return nil, false, err + latestHeader := v.l2Blockchain.CurrentHeader() + if latestHeader.Number.Uint64() < expectedBlockHeight { + log.Info("catching up to chain blocks", "localBlocks", latestHeader.Number, "target", expectedBlockHeight) + return nil, false, errors.New("unknown start block hash") + } else { + log.Info("unknown start block hash", "hash", startState.GlobalState.BlockHash, "batch", startState.GlobalState.Batch) + return nil, false, errors.New("unknown start block hash") } } - cursorHash := cursor.MachineHash() + + blocksValidated := v.blockValidator.BlocksValidated() + + currentL1Block, err := v.client.BlockByNumber(ctx, nil) if err != nil { return nil, false, err } - if cursorHash != startState.MachineHash { - return nil, false, errors.Errorf("local machine doesn't match chain %v %v", cursor.TotalGasConsumed(), startState.TotalGasConsumed) - } - // Not necessarily successors - successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNodeHash, startState.ProposedBlock) + minAssertionPeriod, err := v.rollup.MinimumAssertionPeriod(v.getCallOpts(ctx)) if err != nil { return nil, false, err } - // If there are no successor nodes, and there isn't much activity to process, don't do anything yet - if len(successorNodes) == 0 { - coreGasExecuted, err := v.lookup.GetLastMachineTotalGas() - if err != nil { - return nil, false, err - } - coreSendCount, err := v.lookup.GetSendCount() - if err != nil { - return nil, false, err - } - gasExecuted := new(big.Int).Sub(coreGasExecuted, startState.TotalGasConsumed) - sendCount := new(big.Int).Sub(coreSendCount, startState.TotalSendCount) - if sendCount.Cmp(v.SendThreshold) < 0 && - gasExecuted.Cmp(v.GasThreshold) < 0 && - timeSinceProposed.Cmp(v.BlockThreshold) < 0 { - return nil, false, nil - } - } - - gasesUsed := make([]*big.Int, 0, len(successorNodes)+1) - for _, nd := range successorNodes { - gasesUsed = append(gasesUsed, nd.Assertion.After.TotalGasConsumed) + timeSinceProposed := new(big.Int).Sub(currentL1Block.Number(), new(big.Int).SetUint64(startStateProposed)) + if timeSinceProposed.Cmp(minAssertionPeriod) < 0 { + // Too soon to assert + return nil, false, nil } - arbGasSpeedLimitPerBlock, err := v.rollup.ArbGasSpeedLimitPerBlock(ctx) + // Not necessarily successors + successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNodeHash, startStateProposed) if err != nil { return nil, false, err } - minimumGasToConsume := new(big.Int).Mul(timeSinceProposed, arbGasSpeedLimitPerBlock) - maximumGasTarget := new(big.Int).Mul(minimumGasToConsume, big.NewInt(4)) - maximumGasTarget = maximumGasTarget.Add(maximumGasTarget, startState.TotalGasConsumed) - - if strategy > WatchtowerStrategy { - gasesUsed = append(gasesUsed, maximumGasTarget) - } - - execTracker := core.NewExecutionTrackerWithInitialCursor(v.lookup, false, gasesUsed, cursor, false) - var correctNode nodeAction wrongNodesExist := false if len(successorNodes) > 0 { - logger.Info().Int("count", len(successorNodes)).Msg("examining existing potential successors") + log.Info("examining existing potential successors", "count", len(successorNodes)) } - for nodeI, nd := range successorNodes { + for _, nd := range successorNodes { if correctNode != nil && wrongNodesExist { // We've found everything we could hope to find break } if correctNode == nil { - var batchItemEndAcc common.Hash - if nd.Assertion.After.TotalMessagesRead.Cmp(nd.AfterInboxBatchEndCount) == 0 { - batchItemEndAcc = nd.AfterInboxBatchAcc - } else if nd.Assertion.After.TotalMessagesRead.Cmp(big.NewInt(0)) > 0 { - var haveBatchEndAcc common.Hash - index1 := new(big.Int).Sub(nd.Assertion.After.TotalMessagesRead, big.NewInt(1)) - index2 := new(big.Int).Sub(nd.AfterInboxBatchEndCount, big.NewInt(1)) - batchItemEndAcc, haveBatchEndAcc, err = v.lookup.GetInboxAccPair(index1, index2) - if err != nil { - return nil, false, err - } - if haveBatchEndAcc != nd.AfterInboxBatchAcc { - return nil, false, errors.New("inbox reorg detected by batch end acc mismatch") - } + afterGs := nd.Assertion.AfterState.GlobalState + if afterGs.PosInBatch != 0 { + return nil, false, fmt.Errorf("non-zero position in batch in assertion: batch %v pos %v", afterGs.Batch, afterGs.PosInBatch) } - valid, err := core.IsAssertionValid(nd.Assertion, execTracker, batchItemEndAcc) + lastBlockNum, err := v.blockNumberFromBatchCount(afterGs.Batch) if err != nil { return nil, false, err } + if blocksValidated < lastBlockNum { + return nil, false, fmt.Errorf("waiting for validator to catch up to assertion: %v/%v", blocksValidated, lastBlockNum) + } + lastBlock := v.l2Blockchain.GetBlockByNumber(lastBlockNum) + if lastBlock == nil { + return nil, false, fmt.Errorf("block %v not in database despite being validated", lastBlockNum) + } + lastBlockExtra, err := arbos.DeserializeHeaderExtraInformation(lastBlock.Header()) + if err != nil { + return nil, false, err + } + + valid := nd.Assertion.NumBlocks == lastBlockNum-startBlock.NumberU64() && + afterGs.BlockHash == lastBlock.Hash() && + afterGs.SendRoot == lastBlockExtra.SendRoot if valid { - logger.Info().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found correct node") + log.Info("found correct node", "node", nd.NodeNum) correctNode = existingNodeAction{ number: nd.NodeNum, hash: nd.NodeHash, } - stakerInfo.latestExecutionCursor, err = execTracker.GetExecutionCursor(nd.AfterState().TotalGasConsumed, true) - if err != nil { - return nil, false, err - } - if nodeI != len(successorNodes)-1 && stakerInfo.latestExecutionCursor != nil { - // We will need to use this execution tracker more, so we need to clone this cursor - stakerInfo.latestExecutionCursor = stakerInfo.latestExecutionCursor.Clone() - } continue } else { - logger.Warn().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found node with incorrect assertion") + log.Warn("found node with incorrect assertion", "node", nd.NodeNum) } } else { - logger.Warn().Int("node", int((*big.Int)(nd.NodeNum).Int64())).Msg("found younger sibling to correct node") + log.Warn("found younger sibling to correct node", "node", nd.NodeNum) } // If we've hit this point, the node is "wrong" wrongNodesExist = true @@ -340,21 +337,47 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return correctNode, wrongNodesExist, nil } - execState, _, err := execTracker.GetExecutionState(maximumGasTarget) - if err != nil { - return nil, false, err + if new(big.Int).SetUint64(localBatchCount).Cmp(startState.InboxMaxCount) < 0 { + // not enough batches in database + return nil, wrongNodesExist, nil + } + + var validatedBatchCount uint64 + var validatedBatchBlockNum uint64 + for i := localBatchCount; i > startState.GlobalState.Batch; i-- { + if i == 0 { + break + } + blockNum, err := v.inboxTracker.GetBatchMessageCount(i - 1) + if err != nil { + return nil, false, err + } + blockNum += v.genesisBlockNumber + if blockNum > blocksValidated { + continue + } + validatedBatchCount = i + validatedBatchBlockNum = blockNum + break } - stakerInfo.latestExecutionCursor, err = execTracker.GetExecutionCursor(maximumGasTarget, true) + if validatedBatchCount == 0 { + // we haven't validated any new batches + return nil, wrongNodesExist, nil + } + validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(validatedBatchCount - 1) if err != nil { return nil, false, err } - if new(big.Int).Sub(execState.TotalGasConsumed, startState.TotalGasConsumed).Cmp(minimumGasToConsume) < 0 && execState.TotalMessagesRead.Cmp(startState.InboxMaxCount) < 0 { - // Couldn't execute far enough - return nil, wrongNodesExist, nil + assertingBlock := v.l2Blockchain.GetBlockByNumber(validatedBatchBlockNum) + if assertingBlock == nil { + return nil, false, fmt.Errorf("missing validated block %v", validatedBatchBlockNum) + } + assertingBlockExtra, err := arbos.DeserializeHeaderExtraInformation(assertingBlock.Header()) + if err != nil { + return nil, false, err } - inboxAcc := execState.InboxAcc hasSiblingByte := [1]byte{0} lastNum := stakerInfo.LatestStakedNode lastHash := stakerInfo.LatestStakedNodeHash @@ -364,112 +387,52 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake lastHash = lastSuccessor.NodeHash hasSiblingByte[0] = 1 } - assertion := &core.Assertion{ - Before: startState.ExecutionState, - After: execState, + assertion := &Assertion{ + BeforeState: startState.ExecutionState, + AfterState: &ExecutionState{ + GlobalState: GoGlobalState{ + BlockHash: assertingBlock.Hash(), + SendRoot: assertingBlockExtra.SendRoot, + Batch: localBatchCount, + PosInBatch: 0, + }, + MachineStatus: MachineStatusFinished, + }, + NumBlocks: assertingBlock.NumberU64() - startBlock.NumberU64(), } executionHash := assertion.ExecutionHash() - newNodeHash := hashing.SoliditySHA3(hasSiblingByte[:], lastHash[:], executionHash[:], inboxAcc[:]) - - var seqBatchProof []byte - if execState.TotalMessagesRead.Cmp(big.NewInt(0)) > 0 { - batch, err := v.sequencerInbox.LookupBatchContaining(ctx, v.lookup, new(big.Int).Sub(execState.TotalMessagesRead, big.NewInt(1))) - if err != nil { - return nil, false, err - } - if batch == nil { - return nil, false, errors.New("Failed to lookup batch containing message") - } - seqBatchProof = append(seqBatchProof, math.U256Bytes(batch.GetBatchIndex())...) - proofPart, err := v.generateBatchEndProof(batch.GetBeforeCount()) - if err != nil { - return nil, false, err - } - seqBatchProof = append(seqBatchProof, proofPart...) - proofPart, err = v.generateBatchEndProof(batch.GetAfterCount()) - if err != nil { - return nil, false, err - } - seqBatchProof = append(seqBatchProof, proofPart...) - } + newNodeHash := crypto.Keccak256Hash(hasSiblingByte[:], lastHash[:], executionHash[:], validatedBatchAcc[:]) action := createNodeAction{ - assertion: assertion, - hash: newNodeHash, - prevProposedBlock: startState.ProposedBlock, - prevInboxMaxCount: startState.InboxMaxCount, - sequencerBatchProof: seqBatchProof, + assertion: assertion, + hash: newNodeHash, + prevInboxMaxCount: startState.InboxMaxCount, } - logger.Info().Str("hash", hex.EncodeToString(newNodeHash[:])).Int("lastNode", int(lastNum.Int64())).Int("parentNode", int(stakerInfo.LatestStakedNode.Int64())).Msg("Creating node") + log.Info("creating node", "hash", newNodeHash, "lastNode", lastNum, "parentNode", stakerInfo.LatestStakedNode) return action, wrongNodesExist, nil } -func (v *Validator) generateBatchEndProof(count *big.Int) ([]byte, error) { - if count.Cmp(big.NewInt(0)) == 0 { - return []byte{}, nil - } - var beforeAcc common.Hash - var err error - if count.Cmp(big.NewInt(2)) >= 0 { - beforeAcc, err = v.lookup.GetInboxAcc(new(big.Int).Sub(count, big.NewInt(2))) - if err != nil { - return nil, err - } - } - seqNum := new(big.Int).Sub(count, big.NewInt(1)) - message, err := core.GetSingleMessage(v.lookup, seqNum) - if err != nil { - return nil, err - } - var proof []byte - proof = append(proof, beforeAcc.Bytes()...) - proof = append(proof, math.U256Bytes(seqNum)...) - prefixHash := hashing.SoliditySHA3( - hashing.Address(message.Sender), - hashing.Uint256(message.ChainTime.BlockNum.AsInt()), - hashing.Uint256(message.ChainTime.Timestamp), - ) - proof = append(proof, prefixHash.Bytes()...) - proof = append(proof, hashing.SoliditySHA3(message.Data).Bytes()...) - return proof, nil -} - -func getBlockID(ctx context.Context, client ethutils.EthClient, number *big.Int) (*common.BlockId, error) { - blockInfo, err := client.BlockInfoByNumber(ctx, number) - if err != nil { - return nil, err - } - return &common.BlockId{ - Height: common.NewTimeBlocks((*big.Int)(blockInfo.Number)), - HeaderHash: common.NewHashFromEth(blockInfo.Hash), - }, nil -} - -func lookupNodeStartState(ctx context.Context, rollup *ethbridge.RollupWatcher, nodeNum *big.Int, nodeHash [32]byte) (*core.NodeState, error) { - if nodeNum.Cmp(big.NewInt(0)) == 0 { +func lookupNodeStartState(ctx context.Context, rollup *RollupWatcher, nodeNum uint64, nodeHash [32]byte) (*NodeState, uint64, error) { + if nodeNum == 0 { creationEvent, err := rollup.LookupCreation(ctx) if err != nil { - return nil, err + return nil, 0, err } - return &core.NodeState{ - ProposedBlock: new(big.Int).SetUint64(creationEvent.Raw.BlockNumber), + return &NodeState{ InboxMaxCount: big.NewInt(1), - ExecutionState: &core.ExecutionState{ - TotalGasConsumed: big.NewInt(0), - MachineHash: creationEvent.MachineHash, - TotalMessagesRead: big.NewInt(0), - TotalSendCount: big.NewInt(0), - TotalLogCount: big.NewInt(0), + ExecutionState: &ExecutionState{ + GlobalState: GoGlobalState{}, + MachineStatus: MachineStatusFinished, }, - }, nil + }, creationEvent.Raw.BlockNumber, nil } node, err := rollup.LookupNode(ctx, nodeNum) if err != nil { - return nil, err + return nil, 0, err } if node.NodeHash != nodeHash { - return nil, errors.New("looked up starting node but found wrong hash") + return nil, 0, errors.New("looked up starting node but found wrong hash") } - return node.AfterState(), nil + return node.AfterState(), node.BlockProposed, nil } diff --git a/validator/rollup_watcher.go b/validator/rollup_watcher.go new file mode 100644 index 0000000000..a71dc5ba9b --- /dev/null +++ b/validator/rollup_watcher.go @@ -0,0 +1,217 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "encoding/binary" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" + "github.com/pkg/errors" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi/bind" +) + +var rollupCreatedID common.Hash +var nodeCreatedID common.Hash +var challengeCreatedID common.Hash + +func init() { + parsedRollup, err := rollupgen.RollupUserLogicMetaData.GetAbi() + if err != nil { + panic(err) + } + rollupCreatedID = parsedRollup.Events["RollupCreated"].ID + nodeCreatedID = parsedRollup.Events["NodeCreated"].ID + challengeCreatedID = parsedRollup.Events["RollupChallengeStarted"].ID +} + +type StakerInfo struct { + Index uint64 + LatestStakedNode uint64 + AmountStaked *big.Int + CurrentChallenge *common.Address +} + +type RollupWatcher struct { + address common.Address + fromBlock int64 + client arbutil.L1Interface + baseCallOpts bind.CallOpts + *rollupgen.RollupUserLogic +} + +func NewRollupWatcher(address common.Address, fromBlock int64, client arbutil.L1Interface, callOpts bind.CallOpts) (*RollupWatcher, error) { + con, err := rollupgen.NewRollupUserLogic(address, client) + if err != nil { + return nil, errors.WithStack(err) + } + + return &RollupWatcher{ + address: address, + fromBlock: fromBlock, + client: client, + baseCallOpts: callOpts, + RollupUserLogic: con, + }, nil +} + +func (r *RollupWatcher) getCallOpts(ctx context.Context) *bind.CallOpts { + opts := r.baseCallOpts + opts.Context = ctx + return &opts +} + +func (r *RollupWatcher) LookupCreation(ctx context.Context) (*rollupgen.RollupUserLogicRollupCreated, error) { + var query = ethereum.FilterQuery{ + BlockHash: nil, + FromBlock: big.NewInt(r.fromBlock), + ToBlock: big.NewInt(r.fromBlock), + Addresses: []common.Address{r.address}, + Topics: [][]common.Hash{{rollupCreatedID}}, + } + logs, err := r.client.FilterLogs(ctx, query) + if err != nil { + return nil, errors.WithStack(err) + } + if len(logs) == 0 { + return nil, errors.New("rollup not created") + } + if len(logs) > 1 { + return nil, errors.New("rollup created multiple times") + } + ev, err := r.ParseRollupCreated(logs[0]) + return ev, errors.WithStack(err) +} + +func (r *RollupWatcher) LookupNode(ctx context.Context, number uint64) (*NodeInfo, error) { + var numberAsHash common.Hash + binary.BigEndian.PutUint64(numberAsHash[(32-8):], number) + var query = ethereum.FilterQuery{ + BlockHash: nil, + FromBlock: big.NewInt(r.fromBlock), + ToBlock: nil, + Addresses: []common.Address{r.address}, + Topics: [][]common.Hash{{nodeCreatedID}, {numberAsHash}}, + } + logs, err := r.client.FilterLogs(ctx, query) + if err != nil { + return nil, errors.WithStack(err) + } + if len(logs) == 0 { + return nil, errors.New("Couldn't find requested node") + } + if len(logs) > 1 { + return nil, errors.New("Found multiple instances of requested node") + } + ethLog := logs[0] + parsedLog, err := r.ParseNodeCreated(ethLog) + if err != nil { + return nil, errors.WithStack(err) + } + return &NodeInfo{ + NodeNum: parsedLog.NodeNum, + BlockProposed: ethLog.BlockNumber, + Assertion: NewAssertionFromFields(parsedLog.AssertionBytes32Fields, parsedLog.AssertionIntFields), + InboxMaxCount: parsedLog.InboxMaxCount, + AfterInboxBatchAcc: parsedLog.AfterInboxBatchAcc, + NodeHash: parsedLog.NodeHash, + WasmModuleRoot: parsedLog.WasmModuleRoot, + }, nil +} + +func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, parentHash [32]byte, fromBlock uint64) ([]*NodeInfo, error) { + var query = ethereum.FilterQuery{ + BlockHash: nil, + FromBlock: new(big.Int).SetUint64(fromBlock), + ToBlock: nil, + Addresses: []common.Address{r.address}, + Topics: [][]common.Hash{{nodeCreatedID}, nil, {parentHash}}, + } + logs, err := r.client.FilterLogs(ctx, query) + if err != nil { + return nil, errors.WithStack(err) + } + infos := make([]*NodeInfo, 0, len(logs)) + lastHash := parentHash + for i, ethLog := range logs { + parsedLog, err := r.ParseNodeCreated(ethLog) + if err != nil { + return nil, errors.WithStack(err) + } + lastHashIsSibling := [1]byte{0} + if i > 0 { + lastHashIsSibling[0] = 1 + } + lastHash = crypto.Keccak256Hash(lastHashIsSibling[:], lastHash[:], parsedLog.ExecutionHash[:], parsedLog.AfterInboxBatchAcc[:]) + infos = append(infos, &NodeInfo{ + NodeNum: parsedLog.NodeNum, + BlockProposed: ethLog.BlockNumber, + Assertion: NewAssertionFromFields(parsedLog.AssertionBytes32Fields, parsedLog.AssertionIntFields), + InboxMaxCount: parsedLog.InboxMaxCount, + AfterInboxBatchAcc: parsedLog.AfterInboxBatchAcc, + NodeHash: lastHash, + }) + } + return infos, nil +} + +func (r *RollupWatcher) LookupChallengedNode(ctx context.Context, address common.Address) (uint64, error) { + addressQuery := common.Hash{} + copy(addressQuery[12:], address.Bytes()) + + query := ethereum.FilterQuery{ + BlockHash: nil, + FromBlock: big.NewInt(r.fromBlock), + ToBlock: nil, + Addresses: []common.Address{r.address}, + Topics: [][]common.Hash{{challengeCreatedID}, {addressQuery}}, + } + logs, err := r.client.FilterLogs(ctx, query) + if err != nil { + return 0, errors.WithStack(err) + } + + if len(logs) == 0 { + return 0, errors.New("no matching challenge") + } + + if len(logs) > 1 { + return 0, errors.New("too many matching challenges") + } + + challenge, err := r.ParseRollupChallengeStarted(logs[0]) + if err != nil { + return 0, errors.WithStack(err) + } + + return challenge.ChallengedNode, nil +} + +func (r *RollupWatcher) StakerInfo(ctx context.Context, staker common.Address) (*StakerInfo, error) { + info, err := r.StakerMap(r.getCallOpts(ctx), staker) + if err != nil { + return nil, errors.WithStack(err) + } + if !info.IsStaked { + return nil, nil + } + stakerInfo := &StakerInfo{ + Index: info.Index, + LatestStakedNode: info.LatestStakedNode, + AmountStaked: info.AmountStaked, + } + emptyAddress := common.Address{} + if info.CurrentChallenge != emptyAddress { + chal := info.CurrentChallenge + stakerInfo.CurrentChallenge = &chal + } + return stakerInfo, nil +} diff --git a/validator/staker.go b/validator/staker.go index ac73b13999..28700e4dae 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -11,10 +11,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/pkg/errors" ) @@ -29,21 +31,30 @@ const ( MakeNodesStrategy ) -type nodeAndHash struct { - id *big.Int - hash common.Hash +type L1PostingStrategy struct { + HighGasThreshold float64 + HighGasDelayBlocks int64 +} + +type ValidatorConfig struct { + Strategy string + UtilsAddress string + StakerDelay time.Duration + WalletFactoryAddress string + L1PostingStrategy L1PostingStrategy + DontChallenge bool + WithdrawDestination string + TargetNumMachines int } -type StakerInfo struct { - Index *big.Int - LatestStakedNode *big.Int - AmountStaked *big.Int - CurrentChallenge *common.Address +type nodeAndHash struct { + id uint64 + hash common.Hash } type Staker struct { *Validator - activeChallenge *validator.ChallengeManager + activeChallenge *ChallengeManager strategy StakerStrategy fromBlock int64 baseCallOpts bind.CallOpts @@ -52,22 +63,32 @@ type Staker struct { highGasBlocksBuffer *big.Int lastActCalledBlock *big.Int inactiveLastCheckedNode *nodeAndHash - bringActiveUntilNode *big.Int + bringActiveUntilNode uint64 withdrawDestination common.Address + + l2Blockchain *core.BlockChain + inboxReader InboxReaderInterface + inboxTracker InboxTrackerInterface + txStreamer TransactionStreamerInterface } func NewStaker( ctx context.Context, client *ethclient.Client, - wallet *ethbridge.ValidatorWallet, + wallet *ValidatorWallet, fromBlock int64, validatorUtilsAddress common.Address, strategy StakerStrategy, callOpts bind.CallOpts, auth *bind.TransactOpts, config ValidatorConfig, + l2Blockchain *core.BlockChain, + inboxReader InboxReaderInterface, + inboxTracker InboxTrackerInterface, + txStreamer TransactionStreamerInterface, + blockValidator *BlockValidator, ) (*Staker, error) { - val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts) + val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) if err != nil { return nil, err } @@ -85,6 +106,11 @@ func NewStaker( highGasBlocksBuffer: big.NewInt(config.L1PostingStrategy.HighGasDelayBlocks), lastActCalledBlock: nil, withdrawDestination: withdrawDestination, + + l2Blockchain: l2Blockchain, + inboxReader: inboxReader, + inboxTracker: inboxTracker, + txStreamer: txStreamer, }, nil } @@ -137,12 +163,12 @@ func (s *Staker) shouldAct(ctx context.Context) bool { gasPriceHigh = true } } - latestBlockInfo, err := s.client.BlockInfoByNumber(ctx, nil) + latestBlockInfo, err := s.client.HeaderByNumber(ctx, nil) if err != nil { log.Warn("error getting latest block", "err", err) return true } - latestBlockNum := latestBlockInfo.Number.ToInt() + latestBlockNum := latestBlockInfo.Number if s.lastActCalledBlock == nil { s.lastActCalledBlock = latestBlockNum } @@ -179,6 +205,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { // The fact that we're delaying acting is alreay logged in `shouldAct` return nil, nil } + callOpts := s.getCallOpts(ctx) s.builder.ClearTransactions() var rawInfo *StakerInfo walletAddress := s.wallet.Address() @@ -195,7 +222,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } // If the wallet address is zero, or the wallet address isn't staked, // this will return the latest node and its hash (atomically). - latestStakedNode, latestStakedNodeHash, err := s.validatorUtils.LatestStaked(ctx, walletAddressOrZero) + latestStakedNode, latestStakedNodeHash, err := s.validatorUtils.LatestStaked(callOpts, s.rollupAddress, walletAddressOrZero) if err != nil { return nil, err } @@ -210,7 +237,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } effectiveStrategy := s.strategy - nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(ctx) + nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(callOpts, s.rollupAddress) if err != nil { return nil, err } @@ -221,14 +248,14 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } s.inactiveLastCheckedNode = nil } - if s.bringActiveUntilNode != nil { - if info.LatestStakedNode.Cmp(s.bringActiveUntilNode) < 0 { + if s.bringActiveUntilNode != 0 { + if info.LatestStakedNode < s.bringActiveUntilNode { if effectiveStrategy == DefensiveStrategy { effectiveStrategy = StakeLatestStrategy } } else { log.Info("defensive validator staked past incorrect node; waiting here") - s.bringActiveUntilNode = nil + s.bringActiveUntilNode = 0 } s.inactiveLastCheckedNode = nil } @@ -264,12 +291,12 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { addr := s.wallet.Address() if addr != nil { - withdrawable, err := s.rollup.WithdrawableFunds(ctx, *addr) + withdrawable, err := s.rollup.WithdrawableFunds(callOpts, *addr) if err != nil { return nil, err } if withdrawable.Sign() > 0 && s.withdrawDestination != (common.Address{}) { - err = s.rollup.WithdrawFunds(ctx, s.withdrawDestination) + _, err = s.rollup.WithdrawStakerFunds(s.builder.Auth(ctx), s.withdrawDestination) if err != nil { return nil, err } @@ -278,7 +305,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { // Don't attempt to create a new stake if we're resolving a node, // as that might affect the current required stake. - creatingNewStake := rawInfo == nil && s.builder.TransactionCount() == 0 + creatingNewStake := rawInfo == nil && s.builder.BuilderTransactionCount() == 0 if creatingNewStake { if err := s.newStake(ctx); err != nil { return nil, err @@ -298,13 +325,13 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } } } - if rawInfo != nil && s.builder.TransactionCount() == 0 { + if rawInfo != nil && s.builder.BuilderTransactionCount() == 0 { if err := s.createConflict(ctx, rawInfo); err != nil { return nil, err } } - txCount := s.builder.TransactionCount() + txCount := s.builder.BuilderTransactionCount() if creatingNewStake { // Ignore our stake creation, as it's useless by itself txCount-- @@ -324,10 +351,10 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { return nil } - if s.activeChallenge == nil || s.activeChallenge.ChallengeAddress() != *info.CurrentChallenge { + if s.activeChallenge == nil || s.activeChallenge.RootChallengeAddress() != *info.CurrentChallenge { log.Warn("entered challenge", "challenge", info.CurrentChallenge) - newChallengeManager, err := validator.NewChallengeManager(ctx, s.client, s.auth, *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, s.fromBlock, s.config.TargetNumMachines) + newChallengeManager, err := NewChallengeManager(ctx, s.client, s.auth, *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, uint64(s.fromBlock), s.config.TargetNumMachines) if err != nil { return err } @@ -350,11 +377,15 @@ func (s *Staker) newStake(ctx context.Context) error { return nil } } - stakeAmount, err := s.rollup.CurrentRequiredStake(ctx) + stakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx)) + if err != nil { + return err + } + _, err = s.rollup.NewStake(s.builder.AuthWithAmount(ctx, stakeAmount)) if err != nil { return err } - return s.rollup.NewStake(ctx, stakeAmount) + return nil } func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { @@ -380,16 +411,17 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv if !active { if wrongNodesExist && effectiveStrategy >= DefensiveStrategy { log.Warn("bringing defensive validator online because of incorrect assertion") - s.bringActiveUntilNode = new(big.Int).Add(info.LatestStakedNode, big.NewInt(1)) + s.bringActiveUntilNode = info.LatestStakedNode + 1 } info.CanProgress = false return nil } // Details are already logged with more details in generateNodeAction info.CanProgress = false - info.LatestStakedNode = nil + info.LatestStakedNode = 0 info.LatestStakedNodeHash = action.hash - return s.rollup.StakeOnNewNode(ctx, action.hash, action.assertion, action.prevProposedBlock, action.prevInboxMaxCount, action.sequencerBatchProof) + _, err = s.rollup.StakeOnNewNode(s.builder.Auth(ctx), action.hash, action.assertion.BytesFields(), action.assertion.IntFields(), action.prevInboxMaxCount, action.assertion.NumBlocks) + return err case existingNodeAction: info.LatestStakedNode = action.number info.LatestStakedNodeHash = action.hash @@ -407,7 +439,8 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv return nil } log.Info("staking on existing node", "node", action.number) - return s.rollup.StakeOnExistingNode(ctx, action.number, action.hash) + _, err = s.rollup.StakeOnExistingNode(s.builder.Auth(ctx), action.number, action.hash) + return err default: panic("invalid action type") } @@ -418,11 +451,20 @@ func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { return nil } - stakers, err := s.validatorUtils.GetStakers(ctx) + callOpts := s.getCallOpts(ctx) + stakers, moreStakers, err := s.validatorUtils.GetStakers(callOpts, s.rollupAddress, 0, 1024) if err != nil { return err } - latestNode, err := s.rollup.LatestConfirmedNode(ctx) + for moreStakers { + var newStakers []common.Address + newStakers, moreStakers, err = s.validatorUtils.GetStakers(callOpts, s.rollupAddress, uint64(len(stakers)), 1024) + if err != nil { + return err + } + stakers = append(stakers, newStakers...) + } + latestNode, err := s.rollup.LatestConfirmed(callOpts) if err != nil { return err } @@ -436,40 +478,46 @@ func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { if stakerInfo.CurrentChallenge != nil { continue } - conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(ctx, walletAddr, staker) + conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(callOpts, s.rollupAddress, walletAddr, staker, big.NewInt(1024)) if err != nil { return err } - if conflictType != CONFLICT_TYPE_FOUND { + if ConflictType(conflictType) != CONFLICT_TYPE_FOUND { continue } staker1 := walletAddr staker2 := staker - if node2.Cmp(node1) < 0 { + if node2 < node1 { staker1, staker2 = staker2, staker1 node1, node2 = node2, node1 } - if node1.Cmp(latestNode) <= 0 { + if node1 <= latestNode { // removeOldStakers will take care of them continue } - node1Info, err := s.rollup.RollupWatcher.LookupNode(ctx, node1) + node1Info, err := s.rollup.LookupNode(ctx, node1) if err != nil { return err } - node2Info, err := s.rollup.RollupWatcher.LookupNode(ctx, node2) + node2Info, err := s.rollup.LookupNode(ctx, node2) if err != nil { return err } log.Warn("creating challenge", "ourNode", node1, "otherNode", node2, "otherStaker", staker2) - return s.rollup.CreateChallenge( - ctx, - staker1, - node1Info, - staker2, - node2Info, + _, err = s.rollup.CreateChallenge( + s.builder.Auth(ctx), + [2]common.Address{staker1, staker2}, + [2]uint64{node1, node2}, + [2][2]uint8{node1Info.MachineStatuses(), node2Info.MachineStatuses()}, + [2][2]rollupgen.GlobalState{node1Info.GlobalStates(), node2Info.GlobalStates()}, + [2]uint64{node1Info.Assertion.NumBlocks, node2Info.Assertion.NumBlocks}, + [2]*big.Int{new(big.Int).SetUint64(node1Info.BlockProposed), new(big.Int).SetUint64(node2Info.BlockProposed)}, + [2][32]byte{node1Info.WasmModuleRoot, node2Info.WasmModuleRoot}, ) + if err != nil { + return err + } } // No conflicts exist return nil diff --git a/validator/validator_utils.go b/validator/validator_utils.go new file mode 100644 index 0000000000..6b5663fc5f --- /dev/null +++ b/validator/validator_utils.go @@ -0,0 +1,162 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package validator + +import ( + "context" + "math/big" + + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" + "github.com/pkg/errors" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + common "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +type ConfirmType uint8 + +const ( + CONFIRM_TYPE_NONE ConfirmType = iota + CONFIRM_TYPE_VALID + CONFIRM_TYPE_INVALID +) + +type ConflictType uint8 + +const ( + CONFLICT_TYPE_NONE ConflictType = iota + CONFLICT_TYPE_FOUND + CONFLICT_TYPE_INDETERMINATE + CONFLICT_TYPE_INCOMPLETE +) + +type ValidatorUtils struct { + con *rollupgen.ValidatorUtils + client arbutil.L1Interface + address common.Address + rollupAddress common.Address + baseCallOpts bind.CallOpts +} + +func NewValidatorUtils(address, rollupAddress common.Address, client arbutil.L1Interface, callOpts bind.CallOpts) (*ValidatorUtils, error) { + con, err := rollupgen.NewValidatorUtils(address, client) + if err != nil { + return nil, errors.WithStack(err) + } + + return &ValidatorUtils{ + con: con, + client: client, + address: address, + rollupAddress: rollupAddress, + baseCallOpts: callOpts, + }, nil +} + +func (v *ValidatorUtils) getCallOpts(ctx context.Context) *bind.CallOpts { + opts := v.baseCallOpts + opts.Context = ctx + return &opts +} + +func (v *ValidatorUtils) RefundableStakers(ctx context.Context) ([]common.Address, error) { + addresses, err := v.con.RefundableStakers(v.getCallOpts(ctx), v.rollupAddress) + if err != nil { + return nil, errors.WithStack(err) + } + return addresses, nil +} + +func (v *ValidatorUtils) TimedOutChallenges(ctx context.Context, max int) ([]common.Address, error) { + var count uint64 = 1024 + addresses := make([]common.Address, 0) + for i := uint64(0); ; i += count { + newAddrs, hasMore, err := v.con.TimedOutChallenges(v.getCallOpts(ctx), v.rollupAddress, i, count) + addresses = append(addresses, newAddrs...) + if err != nil { + return nil, errors.WithStack(err) + } + if !hasMore { + break + } + if len(addresses) >= max { + break + } + } + if len(addresses) > max { + addresses = addresses[:max] + } + return addresses, nil +} + +type RollupConfig struct { + ConfirmPeriodBlocks *big.Int + ExtraChallengeTimeBlocks *big.Int + ArbGasSpeedLimitPerBlock *big.Int + BaseStake *big.Int + StakeToken common.Address +} + +func (v *ValidatorUtils) GetStakers(ctx context.Context) ([]common.Address, error) { + addresses, _, err := v.con.GetStakers(v.getCallOpts(ctx), v.rollupAddress, 0, ^uint64(0)) + if err != nil { + return nil, errors.WithStack(err) + } + return addresses, nil +} + +func (v *ValidatorUtils) LatestStaked(ctx context.Context, staker common.Address) (uint64, [32]byte, error) { + amount, hash, err := v.con.LatestStaked(v.getCallOpts(ctx), v.rollupAddress, staker) + return amount, hash, errors.WithStack(err) +} + +func (v *ValidatorUtils) StakedNodes(ctx context.Context, staker common.Address) ([]uint64, error) { + nodes, err := v.con.StakedNodes(v.getCallOpts(ctx), v.rollupAddress, staker) + return nodes, errors.WithStack(err) +} + +func (v *ValidatorUtils) AreUnresolvedNodesLinear(ctx context.Context) (bool, error) { + linear, err := v.con.AreUnresolvedNodesLinear(v.getCallOpts(ctx), v.rollupAddress) + return linear, errors.WithStack(err) +} + +func (v *ValidatorUtils) CheckDecidableNextNode(ctx context.Context) (ConfirmType, error) { + confirmType, err := v.con.CheckDecidableNextNode( + v.getCallOpts(ctx), + v.rollupAddress, + ) + if err != nil { + return CONFIRM_TYPE_NONE, errors.WithStack(err) + } + return ConfirmType(confirmType), nil +} + +func (v *ValidatorUtils) FindStakerConflict(ctx context.Context, staker1, staker2 common.Address) (ConflictType, uint64, uint64, error) { + conflictType, staker1Node, staker2Node, err := v.con.FindStakerConflict( + v.getCallOpts(ctx), + v.rollupAddress, + staker1, + staker2, + math.MaxBig256, + ) + if err != nil { + return CONFLICT_TYPE_NONE, 0, 0, errors.WithStack(err) + } + for ConflictType(conflictType) == CONFLICT_TYPE_INCOMPLETE { + conflictType, staker1Node, staker2Node, err = v.con.FindNodeConflict( + v.getCallOpts(ctx), + v.rollupAddress, + staker1Node, + staker2Node, + math.MaxBig256, + ) + if err != nil { + return CONFLICT_TYPE_NONE, 0, 0, errors.WithStack(err) + } + } + return ConflictType(conflictType), staker1Node, staker2Node, nil +} diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go index 1b7bac9c66..a2f777542b 100644 --- a/validator/validator_wallet.go +++ b/validator/validator_wallet.go @@ -47,7 +47,7 @@ type ValidatorWallet struct { rollupFromBlock int64 } -func NewValidator(address *common.Address, walletFactoryAddr, rollupAddress common.Address, client arbutil.L1Interface, auth *bind.TransactOpts, rollupFromBlock int64, onWalletCreated func(common.Address)) (*ValidatorWallet, error) { +func NewValidatorWallet(address *common.Address, walletFactoryAddr, rollupAddress common.Address, client arbutil.L1Interface, auth *bind.TransactOpts, rollupFromBlock int64, onWalletCreated func(common.Address)) (*ValidatorWallet, error) { var con *rollupgen.ValidatorWallet if address != nil { var err error @@ -103,7 +103,7 @@ func (v *ValidatorWallet) createWalletIfNeeded(ctx context.Context) error { v.onWalletCreated(addr) } } - con, err := rollupgen.NewValidator(*v.address, v.client) + con, err := rollupgen.NewValidatorWallet(*v.address, v.client) if err != nil { return err } From 6729b8be2964d4aba8594246a6b732eb1ce58e5e Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 15 Feb 2022 19:27:03 -0600 Subject: [PATCH 004/110] Fix TestSequencerCompensation in merge --- system_tests/seqcompensation_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/system_tests/seqcompensation_test.go b/system_tests/seqcompensation_test.go index c8a23f6204..9f605dda3f 100644 --- a/system_tests/seqcompensation_test.go +++ b/system_tests/seqcompensation_test.go @@ -11,8 +11,8 @@ import ( "time" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbos/l1pricing" + "github.com/offchainlabs/arbstate/arbutil" ) // Sequencer address gets something for posting batches @@ -29,7 +29,7 @@ func TestSequencerCompensation(t *testing.T) { tx := l2info.PrepareTx("Owner", "User2", l2info.TransferGas, big.NewInt(1e12), nil) err := l2clientA.SendTransaction(ctx, tx) Require(t, err) - _, err = arbnode.EnsureTxSucceeded(ctx, l2clientA, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) // give the inbox reader a bit of time to pick up the delayed message @@ -42,7 +42,7 @@ func TestSequencerCompensation(t *testing.T) { }) } - _, err = arbnode.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) + _, err = arbutil.WaitForTx(ctx, l2clientB, tx.Hash(), time.Second*5) Require(t, err) // clientB sees balance means sequencer message was sent From 738c5d757595be29b402ff8473d8475d98923dae Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 15 Feb 2022 19:56:40 -0600 Subject: [PATCH 005/110] Have RollupCore set inboxMaxCount of assertion --- solgen/src/rollup/RollupCore.sol | 10 +++++----- solgen/src/rollup/RollupLib.sol | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 0345bbfdb4..352275c712 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -564,7 +564,7 @@ abstract contract RollupCore is IRollupCore, Cloneable, Pausable { } function createNewNode( - RollupLib.Assertion calldata assertion, + RollupLib.Assertion memory assertion, uint64 prevNodeNum, bytes32 expectedNodeHash ) internal returns (bytes32 newNodeHash) { @@ -579,12 +579,12 @@ abstract contract RollupCore is IRollupCore, Cloneable, Pausable { // validate data memoryFrame.prevNode = getNode(prevNodeNum); memoryFrame.currentInboxSize = sequencerBridge.batchCount(); - // ensure that the assertion specified the correct inbox size - require(assertion.afterState.inboxMaxCount == memoryFrame.currentInboxSize, "WRONG_INBOX_COUNT"); + // set the inbox max count according to the current inbox size + assertion.afterState.inboxMaxCount = memoryFrame.currentInboxSize; // Make sure the previous state is correct against the node being built on require( - RollupLib.stateHash(assertion.beforeState) == + RollupLib.stateHashMem(assertion.beforeState) == memoryFrame.prevNode.stateHash, "PREV_STATE_HASH" ); @@ -640,7 +640,7 @@ abstract contract RollupCore is IRollupCore, Cloneable, Pausable { require(newNodeHash == expectedNodeHash, "UNEXPECTED_NODE_HASH"); memoryFrame.node = NodeLib.initialize( - RollupLib.stateHash(assertion.afterState), + RollupLib.stateHashMem(assertion.afterState), RollupLib.challengeRootHash( memoryFrame.executionHash, block.number, diff --git a/solgen/src/rollup/RollupLib.sol b/solgen/src/rollup/RollupLib.sol index 2eb1d0a39d..c64407ac6f 100644 --- a/solgen/src/rollup/RollupLib.sol +++ b/solgen/src/rollup/RollupLib.sol @@ -79,7 +79,7 @@ library RollupLib { uint64 numBlocks; } - function executionHash(Assertion calldata assertion) + function executionHash(Assertion memory assertion) internal pure returns (bytes32) @@ -127,7 +127,7 @@ library RollupLib { ); } - function confirmHash(Assertion calldata assertion) + function confirmHash(Assertion memory assertion) internal pure returns (bytes32) From 1c887ff2508f52cf95e4530ff996f11940ec3d0f Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 15 Feb 2022 20:24:48 -0600 Subject: [PATCH 006/110] Support non-zero PosInBatch of assertions --- validator/assertion.go | 23 +++++--- validator/l1_validator.go | 108 ++++++++++++++++++++++++-------------- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/validator/assertion.go b/validator/assertion.go index de0a9a4668..ca16d4b59c 100644 --- a/validator/assertion.go +++ b/validator/assertion.go @@ -54,18 +54,27 @@ func (s *ExecutionState) AsSolidityStruct() rollupgen.RollupLibExecutionState { } } -func (a *ExecutionState) BlockStateHash() common.Hash { - if a.MachineStatus == MachineStatusFinished { - return crypto.Keccak256Hash([]byte("Block state:"), a.GlobalState.Hash().Bytes()) - } else if a.MachineStatus == MachineStatusErrored { - return crypto.Keccak256Hash([]byte("Block state, errored:"), a.GlobalState.Hash().Bytes()) - } else if a.MachineStatus == MachineStatusTooFar { +func (s *ExecutionState) BlockStateHash() common.Hash { + if s.MachineStatus == MachineStatusFinished { + return crypto.Keccak256Hash([]byte("Block state:"), s.GlobalState.Hash().Bytes()) + } else if s.MachineStatus == MachineStatusErrored { + return crypto.Keccak256Hash([]byte("Block state, errored:"), s.GlobalState.Hash().Bytes()) + } else if s.MachineStatus == MachineStatusTooFar { return crypto.Keccak256Hash([]byte("Block state, too far:")) } else { - panic(fmt.Sprintf("invalid machine status %v", a.MachineStatus)) + panic(fmt.Sprintf("invalid machine status %v", s.MachineStatus)) } } +func (s *ExecutionState) RequiredBatches() uint64 { + count := s.GlobalState.Batch + if (s.MachineStatus == MachineStatusErrored || s.GlobalState.PosInBatch > 0) && count < math.MaxUint64 { + // The current batch was read + count++ + } + return count +} + func (a *Assertion) AsSolidityStruct() rollupgen.RollupLibAssertion { return rollupgen.RollupLibAssertion{ BeforeState: a.BeforeState.AsSolidityStruct(), diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 45a4e7b5e0..e80b2a2dd3 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -215,16 +215,29 @@ type OurStakerInfo struct { *StakerInfo } -func (v *Validator) blockNumberFromBatchCount(batch uint64) (uint64, error) { - var height uint64 - if batch > 0 { +// Returns (block number, global state inbox position is invalid, error). +// If global state is invalid, block number is set to the last of the batch. +func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (uint64, bool, error) { + var batchHeight uint64 + if gs.Batch > 0 { var err error - height, err = v.inboxTracker.GetBatchMessageCount(batch - 1) + batchHeight, err = v.inboxTracker.GetBatchMessageCount(gs.Batch - 1) if err != nil { - return 0, err + return 0, false, err } } - return height + v.genesisBlockNumber, nil + + nextBatchHeight, err := v.inboxTracker.GetBatchMessageCount(gs.Batch) + if err != nil { + return 0, false, err + } + + if gs.PosInBatch >= nextBatchHeight-batchHeight { + // This PosInBatch would enter the next batch. Return the last block before the next batch. + return v.genesisBlockNumber + nextBatchHeight - 1, true, nil + } + + return v.genesisBlockNumber + batchHeight + gs.PosInBatch, false, nil } func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy, fromBlock int64) (nodeAction, bool, error) { @@ -237,23 +250,27 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake if err != nil { return nil, false, err } - if localBatchCount < startState.GlobalState.Batch { - log.Info("catching up to chain batches", "localBatches", localBatchCount, "target", startState.GlobalState.Batch) + if localBatchCount < startState.RequiredBatches() { + log.Info("catching up to chain batches", "localBatches", localBatchCount, "target", startState.RequiredBatches()) return nil, false, nil } startBlock := v.l2Blockchain.GetBlockByHash(startState.GlobalState.BlockHash) if startBlock == nil { - expectedBlockHeight, err := v.blockNumberFromBatchCount(startState.GlobalState.Batch) + expectedBlockHeight, inboxPositionInvalid, err := v.blockNumberFromGlobalState(startState.GlobalState) if err != nil { return nil, false, err } + if inboxPositionInvalid { + log.Error("invalid start global state inbox position", startState.GlobalState.BlockHash, "batch", startState.GlobalState.Batch, "pos", startState.GlobalState.PosInBatch) + return nil, false, errors.New("invalid start global state inbox position") + } latestHeader := v.l2Blockchain.CurrentHeader() if latestHeader.Number.Uint64() < expectedBlockHeight { log.Info("catching up to chain blocks", "localBlocks", latestHeader.Number, "target", expectedBlockHeight) - return nil, false, errors.New("unknown start block hash") + return nil, false, nil } else { - log.Info("unknown start block hash", "hash", startState.GlobalState.BlockHash, "batch", startState.GlobalState.Batch) + log.Error("unknown start block hash", "hash", startState.GlobalState.BlockHash, "batch", startState.GlobalState.Batch, "pos", startState.GlobalState.PosInBatch) return nil, false, errors.New("unknown start block hash") } } @@ -293,11 +310,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake break } if correctNode == nil { - afterGs := nd.Assertion.AfterState.GlobalState - if afterGs.PosInBatch != 0 { - return nil, false, fmt.Errorf("non-zero position in batch in assertion: batch %v pos %v", afterGs.Batch, afterGs.PosInBatch) - } - lastBlockNum, err := v.blockNumberFromBatchCount(afterGs.Batch) + afterGs := nd.AfterState().GlobalState + lastBlockNum, inboxPositionInvalid, err := v.blockNumberFromGlobalState(afterGs) if err != nil { return nil, false, err } @@ -313,7 +327,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return nil, false, err } - valid := nd.Assertion.NumBlocks == lastBlockNum-startBlock.NumberU64() && + valid := !inboxPositionInvalid && + nd.Assertion.NumBlocks == lastBlockNum-startBlock.NumberU64() && afterGs.BlockHash == lastBlock.Hash() && afterGs.SendRoot == lastBlockExtra.SendRoot if valid { @@ -337,41 +352,56 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return correctNode, wrongNodesExist, nil } - if new(big.Int).SetUint64(localBatchCount).Cmp(startState.InboxMaxCount) < 0 { + if !startState.InboxMaxCount.IsUint64() { + return nil, false, fmt.Errorf("inbox max count %v isn't a uint64", startState.InboxMaxCount) + } + minBatchCount := startState.InboxMaxCount.Uint64() + if localBatchCount < minBatchCount { // not enough batches in database return nil, wrongNodesExist, nil } - var validatedBatchCount uint64 - var validatedBatchBlockNum uint64 - for i := localBatchCount; i > startState.GlobalState.Batch; i-- { - if i == 0 { - break + if blocksValidated == 0 || localBatchCount == 0 { + // we haven't validated anything + return nil, wrongNodesExist, nil + } + lastBlockValidated := blocksValidated - 1 + if lastBlockValidated <= startBlock.NumberU64() { + // we haven't validated any new blocks + return nil, wrongNodesExist, nil + } + var assertingBatchNumber uint64 + var assertingPosInBatch uint64 + for i := localBatchCount - 1; i+1 >= minBatchCount && i > 0; i-- { + lastBlockNum, err := v.inboxTracker.GetBatchMessageCount(i) + if err != nil { + return nil, false, err } - blockNum, err := v.inboxTracker.GetBatchMessageCount(i - 1) + lastBlockNum += v.genesisBlockNumber + prevBlockNum, err := v.inboxTracker.GetBatchMessageCount(i - 1) if err != nil { return nil, false, err } - blockNum += v.genesisBlockNumber - if blockNum > blocksValidated { - continue + prevBlockNum += v.genesisBlockNumber + if lastBlockValidated > prevBlockNum && lastBlockValidated <= lastBlockNum { + // We found the batch containing the last validated block + if i+1 == minBatchCount && lastBlockValidated < lastBlockNum { + // We haven't reached the minimum assertion size yet + break + } + assertingBatchNumber = i + assertingPosInBatch = lastBlockValidated - prevBlockNum - 1 + break } - validatedBatchCount = i - validatedBatchBlockNum = blockNum - break - } - if validatedBatchCount == 0 { - // we haven't validated any new batches - return nil, wrongNodesExist, nil } - validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(validatedBatchCount - 1) + validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(assertingBatchNumber) if err != nil { return nil, false, err } - assertingBlock := v.l2Blockchain.GetBlockByNumber(validatedBatchBlockNum) + assertingBlock := v.l2Blockchain.GetBlockByNumber(lastBlockValidated) if assertingBlock == nil { - return nil, false, fmt.Errorf("missing validated block %v", validatedBatchBlockNum) + return nil, false, fmt.Errorf("missing validated block %v", lastBlockValidated) } assertingBlockExtra, err := arbos.DeserializeHeaderExtraInformation(assertingBlock.Header()) if err != nil { @@ -393,8 +423,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake GlobalState: GoGlobalState{ BlockHash: assertingBlock.Hash(), SendRoot: assertingBlockExtra.SendRoot, - Batch: localBatchCount, - PosInBatch: 0, + Batch: assertingBatchNumber, + PosInBatch: assertingPosInBatch, }, MachineStatus: MachineStatusFinished, InboxMaxCount: new(big.Int), // filled in by the contract From c2dc413a3bade4ae39359629f5cc409e9edc952e Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 15 Feb 2022 22:41:49 -0600 Subject: [PATCH 007/110] Begin staker test --- ...idator_test.go => block_validator_test.go} | 2 +- system_tests/staker_test.go | 97 +++++++++++++++++++ validator/l1_validator.go | 7 +- 3 files changed, 104 insertions(+), 2 deletions(-) rename system_tests/{validator_test.go => block_validator_test.go} (97%) create mode 100644 system_tests/staker_test.go diff --git a/system_tests/validator_test.go b/system_tests/block_validator_test.go similarity index 97% rename from system_tests/validator_test.go rename to system_tests/block_validator_test.go index a07f8c597d..6f624754b9 100644 --- a/system_tests/validator_test.go +++ b/system_tests/block_validator_test.go @@ -18,7 +18,7 @@ import ( "github.com/offchainlabs/arbstate/arbutil" ) -func TestValidatorSimple(t *testing.T) { +func TestBlockValidatorSimple(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() l2info, node1, l2client, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go new file mode 100644 index 0000000000..02ad3860d2 --- /dev/null +++ b/system_tests/staker_test.go @@ -0,0 +1,97 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +// race detection makes things slow and miss timeouts +//go:build !race +// +build !race + +package arbtest + +import ( + "context" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/arbstate/arbutil" + "github.com/offchainlabs/arbstate/solgen/go/rollupgen" + "github.com/offchainlabs/arbstate/validator" +) + +func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) { + ctx := context.Background() + l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) + defer l1stack.Close() + + l2clientB, l2nodeB := Create2ndNode(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, false) + + deployAuth := l1info.GetDefaultTransactOpts("RollupOwner") + + valWalletFactory, tx, _, err := rollupgen.DeployValidatorWalletCreator(&deployAuth, l1client) + Require(t, err) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l1client, tx, time.Second*5) + Require(t, err) + + valUtils, tx, _, err := rollupgen.DeployValidatorUtils(&deployAuth, l1client) + Require(t, err) + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, l1client, tx, time.Second*5) + Require(t, err) + + balance := big.NewInt(params.Ether) + balance.Mul(balance, big.NewInt(100)) + l1info.GenerateAccount("ValidatorA") + TransferBalance(t, "Faucet", "ValidatorA", balance, l1info, l1client, ctx) + l1authA := l1info.GetDefaultTransactOpts("ValidatorA") + + l1info.GenerateAccount("ValidatorB") + TransferBalance(t, "Faucet", "ValidatorB", balance, l1info, l1client, ctx) + l1authB := l1info.GetDefaultTransactOpts("ValidatorB") + + valWalletA, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeA.DeployInfo.Rollup, l1client, &l1authA, 0, func(common.Address) {}) + stakerA, err := validator.NewStaker( + ctx, + l1client, + valWalletA, + 0, + valUtils, + validator.MakeNodesStrategy, + bind.CallOpts{}, + &l1authA, + validator.ValidatorConfig{}, + l2nodeA.ArbInterface.BlockChain(), + l2nodeA.InboxReader, + l2nodeA.InboxTracker, + l2nodeA.TxStreamer, + l2nodeA.BlockValidator, + ) + Require(t, err) + + valWalletB, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeB.DeployInfo.Rollup, l1client, &l1authB, 0, func(common.Address) {}) + stakerB, err := validator.NewStaker( + ctx, + l1client, + valWalletB, + 0, + valUtils, + validator.MakeNodesStrategy, + bind.CallOpts{}, + &l1authB, + validator.ValidatorConfig{}, + l2nodeB.ArbInterface.BlockChain(), + l2nodeB.InboxReader, + l2nodeB.InboxTracker, + l2nodeB.TxStreamer, + l2nodeB.BlockValidator, + ) + Require(t, err) + + _, _, _, _ = l2clientA, l2clientB, stakerA, stakerB +} + +func TestStakersCooperative(t *testing.T) { + stakerTestImpl(t, false, false) +} diff --git a/validator/l1_validator.go b/validator/l1_validator.go index e80b2a2dd3..9b4dc18d25 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -275,7 +275,12 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } } - blocksValidated := v.blockValidator.BlocksValidated() + var blocksValidated uint64 + if v.blockValidator != nil { + blocksValidated = v.blockValidator.BlocksValidated() + } else { + blocksValidated = v.l2Blockchain.CurrentHeader().Number.Uint64() + } currentL1Block, err := v.client.BlockByNumber(ctx, nil) if err != nil { From 481f39415e57ec6fbaad3fbf68abe672b0ad54ce Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 11:05:21 -0600 Subject: [PATCH 008/110] Fix BlockChallenge maxInboxMessagesRead --- solgen/src/challenge/BlockChallenge.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/solgen/src/challenge/BlockChallenge.sol b/solgen/src/challenge/BlockChallenge.sol index 41d93551aa..88b2d50f66 100644 --- a/solgen/src/challenge/BlockChallenge.sol +++ b/solgen/src/challenge/BlockChallenge.sol @@ -157,8 +157,12 @@ contract BlockChallenge is ChallengeCore, IChallengeResultReceiver { globalStateHashes[1] ); + uint256 maxInboxMessagesRead = startAndEndGlobalStates[1].getInboxPosition(); + if (machineStatuses[1] == MachineStatus.ERRORED || startAndEndGlobalStates[1].getPositionInMessage() > 0) { + maxInboxMessagesRead++; + } ExecutionContext memory execCtx = ExecutionContext({ - maxInboxMessagesRead: startAndEndGlobalStates[1].getInboxPosition(), + maxInboxMessagesRead: maxInboxMessagesRead, sequencerInbox: sequencerInbox, delayedBridge: delayedBridge }); From 7a81cc27ad774fccbc331aed66b804f2e786d0c9 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 11:05:39 -0600 Subject: [PATCH 009/110] Add missing requires --- system_tests/staker_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 02ad3860d2..551768d618 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -52,6 +52,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) l1authB := l1info.GetDefaultTransactOpts("ValidatorB") valWalletA, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeA.DeployInfo.Rollup, l1client, &l1authA, 0, func(common.Address) {}) + Require(t, err) stakerA, err := validator.NewStaker( ctx, l1client, @@ -71,6 +72,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) Require(t, err) valWalletB, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeB.DeployInfo.Rollup, l1client, &l1authB, 0, func(common.Address) {}) + Require(t, err) stakerB, err := validator.NewStaker( ctx, l1client, From c88a32200262a293d72b33c84cd8cf3b47b8c90c Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 17:02:50 -0600 Subject: [PATCH 010/110] Continue staker test (failing atm) --- system_tests/staker_test.go | 81 ++++++++++++++++++++++++++++++++--- validator/builder_backend.go | 18 ++++++-- validator/l1_validator.go | 21 +++++++-- validator/rollup_watcher.go | 12 ++++-- validator/staker.go | 6 ++- validator/validator_wallet.go | 10 ++--- 6 files changed, 124 insertions(+), 24 deletions(-) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 551768d618..e1f7722511 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -23,7 +23,8 @@ import ( ) func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) { - ctx := context.Background() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) defer l1stack.Close() @@ -51,6 +52,29 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) TransferBalance(t, "Faucet", "ValidatorB", balance, l1info, l1client, ctx) l1authB := l1info.GetDefaultTransactOpts("ValidatorB") + valWalletAddrA, err := validator.CreateValidatorWallet(ctx, valWalletFactory, 0, &l1authA, l1client) + Require(t, err) + valWalletAddrCheck, err := validator.CreateValidatorWallet(ctx, valWalletFactory, 0, &l1authA, l1client) + Require(t, err) + if valWalletAddrA == valWalletAddrCheck { + Require(t, err, "didn't cache validator wallet address", valWalletAddrA.String(), "vs", valWalletAddrCheck.String()) + } + + valWalletAddrB, err := validator.CreateValidatorWallet(ctx, valWalletFactory, 0, &l1authB, l1client) + Require(t, err) + + rollup, err := rollupgen.NewRollupAdminLogic(l2nodeA.DeployInfo.Rollup, l1client) + Require(t, err) + tx, err = rollup.SetValidator(&deployAuth, []common.Address{valWalletAddrA, valWalletAddrB}, []bool{true, true}) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) + Require(t, err) + + valConfig := validator.ValidatorConfig{ + UtilsAddress: valUtils.Hex(), + TargetNumMachines: 4, + } + valWalletA, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeA.DeployInfo.Rollup, l1client, &l1authA, 0, func(common.Address) {}) Require(t, err) stakerA, err := validator.NewStaker( @@ -58,11 +82,10 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) l1client, valWalletA, 0, - valUtils, validator.MakeNodesStrategy, bind.CallOpts{}, &l1authA, - validator.ValidatorConfig{}, + valConfig, l2nodeA.ArbInterface.BlockChain(), l2nodeA.InboxReader, l2nodeA.InboxTracker, @@ -78,11 +101,10 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) l1client, valWalletB, 0, - valUtils, validator.MakeNodesStrategy, bind.CallOpts{}, &l1authB, - validator.ValidatorConfig{}, + valConfig, l2nodeB.ArbInterface.BlockChain(), l2nodeB.InboxReader, l2nodeB.InboxTracker, @@ -91,7 +113,54 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) ) Require(t, err) - _, _, _, _ = l2clientA, l2clientB, stakerA, stakerB + // Continually make L2 transactions in a background thread + l2info.GenerateAccount("BackgroundUser") + tx = l2info.PrepareTx("Faucet", "BackgroundUser", l2info.TransferGas, balance, nil) + err = l2clientA.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) + Require(t, err) + err = l2clientB.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) + Require(t, err) + go (func() { + for i := uint64(0); ctx.Err() == nil; i++ { + l2info.Accounts["BackgroundUser"].Nonce = i + tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) + err := l2clientA.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) + Require(t, err) + if createNodesFlaky || stakeLatestFlaky { + l2info.Accounts["BackgroundUser"].Nonce = i + tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) + } + err = l2clientB.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) + Require(t, err) + } + })() + + for i := 0; i < 100; i++ { + var stakerName string + if i%2 == 0 { + stakerName = "A" + tx, err = stakerA.Act(ctx) + } else { + stakerName = "B" + tx, err = stakerB.Act(ctx) + } + Require(t, err, "Staker", stakerName, "failed to act") + if tx != nil { + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) + Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") + } + for j := 0; j < 20; j++ { + TransferBalance(t, "Faucet", "Faucet", common.Big0, l1info, l1client, ctx) + } + } } func TestStakersCooperative(t *testing.T) { diff --git a/validator/builder_backend.go b/validator/builder_backend.go index 44178bb8f4..8e55ab7c30 100644 --- a/validator/builder_backend.go +++ b/validator/builder_backend.go @@ -21,7 +21,7 @@ type BuilderBackend struct { transactions []*types.Transaction builderAuth *bind.TransactOpts realSender common.Address - wallet *common.Address + wallet *ValidatorWallet arbutil.L1Interface } @@ -38,7 +38,7 @@ func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { return &BuilderBackend{ builderAuth: fakeAuth, realSender: wallet.From(), - wallet: wallet.Address(), + wallet: wallet, L1Interface: wallet.client, }, nil } @@ -51,10 +51,20 @@ func (b *BuilderBackend) ClearTransactions() { b.transactions = nil } +func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { + if call.From == b.builderAuth.From { + if b.wallet.Address() == nil { + return 0, nil + } + call.From = *b.wallet.Address() + } + return b.EstimateGas(ctx, call) +} + func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { b.transactions = append(b.transactions, tx) data, dest, amount, totalAmount := combineTxes(b.transactions) - if b.wallet == nil { + if b.wallet.Address() == nil { return nil } realData, err := validatorABI.Pack("executeTransactions", data, dest, amount) @@ -63,7 +73,7 @@ func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transact } msg := ethereum.CallMsg{ From: b.realSender, - To: b.wallet, + To: b.wallet.Address(), Value: totalAmount, Data: realData, } diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 44f8f013f8..e83f1eb362 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -99,6 +99,7 @@ func NewValidator( BlockThreshold: big.NewInt(960), callOpts: callOpts, genesisBlockNumber: genesisBlockNumber, + l2Blockchain: l2Blockchain, inboxReader: inboxReader, inboxTracker: inboxTracker, txStreamer: txStreamer, @@ -256,7 +257,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } startBlock := v.l2Blockchain.GetBlockByHash(startState.GlobalState.BlockHash) - if startBlock == nil { + if startBlock == nil && (startState.GlobalState != GoGlobalState{}) { expectedBlockHeight, inboxPositionInvalid, err := v.blockNumberFromGlobalState(startState.GlobalState) if err != nil { return nil, false, err @@ -332,8 +333,14 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return nil, false, err } + var expectedNumBlocks uint64 + if startBlock == nil { + expectedNumBlocks = lastBlockNum + 1 + } else { + expectedNumBlocks = lastBlockNum - startBlock.NumberU64() + } valid := !inboxPositionInvalid && - nd.Assertion.NumBlocks == lastBlockNum-startBlock.NumberU64() && + nd.Assertion.NumBlocks == expectedNumBlocks && afterGs.BlockHash == lastBlock.Hash() && afterGs.SendRoot == lastBlockExtra.SendRoot if valid { @@ -371,7 +378,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return nil, wrongNodesExist, nil } lastBlockValidated := blocksValidated - 1 - if lastBlockValidated <= startBlock.NumberU64() { + if startBlock != nil && lastBlockValidated <= startBlock.NumberU64() { // we haven't validated any new blocks return nil, wrongNodesExist, nil } @@ -422,6 +429,12 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake lastHash = lastSuccessor.NodeHash hasSiblingByte[0] = 1 } + var assertionNumBlocks uint64 + if startBlock == nil { + assertionNumBlocks = assertingBlock.NumberU64() + 1 + } else { + assertionNumBlocks = assertingBlock.NumberU64() - startBlock.NumberU64() + } assertion := &Assertion{ BeforeState: startState, AfterState: &ExecutionState{ @@ -433,7 +446,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake }, MachineStatus: MachineStatusFinished, }, - NumBlocks: assertingBlock.NumberU64() - startBlock.NumberU64(), + NumBlocks: assertionNumBlocks, } executionHash := assertion.ExecutionHash() diff --git a/validator/rollup_watcher.go b/validator/rollup_watcher.go index e91d293176..9f3de908df 100644 --- a/validator/rollup_watcher.go +++ b/validator/rollup_watcher.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" ) -var rollupCreatedID common.Hash +var rollupInitializedID common.Hash var nodeCreatedID common.Hash var challengeCreatedID common.Hash @@ -27,7 +27,7 @@ func init() { if err != nil { panic(err) } - rollupCreatedID = parsedRollup.Events["RollupCreated"].ID + rollupInitializedID = parsedRollup.Events["RollupInitialized"].ID nodeCreatedID = parsedRollup.Events["NodeCreated"].ID challengeCreatedID = parsedRollup.Events["RollupChallengeStarted"].ID } @@ -69,12 +69,16 @@ func (r *RollupWatcher) getCallOpts(ctx context.Context) *bind.CallOpts { } func (r *RollupWatcher) LookupCreation(ctx context.Context) (*rollupgen.RollupUserLogicRollupInitialized, error) { + var toBlock *big.Int + if r.fromBlock > 0 { + toBlock = big.NewInt(r.fromBlock) + } var query = ethereum.FilterQuery{ BlockHash: nil, FromBlock: big.NewInt(r.fromBlock), - ToBlock: big.NewInt(r.fromBlock), + ToBlock: toBlock, Addresses: []common.Address{r.address}, - Topics: [][]common.Hash{{rollupCreatedID}}, + Topics: [][]common.Hash{{rollupInitializedID}}, } logs, err := r.client.FilterLogs(ctx, query) if err != nil { diff --git a/validator/staker.go b/validator/staker.go index f0222e986b..5afbbf2206 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -6,6 +6,7 @@ package validator import ( "context" + "fmt" "math/big" "time" @@ -76,7 +77,6 @@ func NewStaker( client *ethclient.Client, wallet *ValidatorWallet, fromBlock int64, - validatorUtilsAddress common.Address, strategy StakerStrategy, callOpts bind.CallOpts, auth *bind.TransactOpts, @@ -87,6 +87,10 @@ func NewStaker( txStreamer TransactionStreamerInterface, blockValidator *BlockValidator, ) (*Staker, error) { + if !common.IsHexAddress(config.UtilsAddress) { + return nil, fmt.Errorf("invalid validator utils address \"%v\"", config.UtilsAddress) + } + validatorUtilsAddress := common.HexToAddress(config.UtilsAddress) val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) if err != nil { return nil, err diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go index a2f777542b..f70e2b2e79 100644 --- a/validator/validator_wallet.go +++ b/validator/validator_wallet.go @@ -133,6 +133,11 @@ func (v *ValidatorWallet) ExecuteTransactions(ctx context.Context, builder *Buil return nil, nil } + err := v.createWalletIfNeeded(ctx) + if err != nil { + return nil, err + } + if len(txes) == 1 { arbTx, err := v.executeTransaction(ctx, txes[0]) if err != nil { @@ -154,11 +159,6 @@ func (v *ValidatorWallet) ExecuteTransactions(ctx context.Context, builder *Buil totalAmount = totalAmount.Add(totalAmount, tx.Value()) } - err := v.createWalletIfNeeded(ctx) - if err != nil { - return nil, err - } - oldAuthValue := v.auth.Value v.auth.Value = totalAmount defer (func() { v.auth.Value = oldAuthValue })() From b34ca09a8ad88b479c823b44f057e93f7d26aa5d Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 17:35:31 -0600 Subject: [PATCH 011/110] Test passing! --- system_tests/staker_test.go | 48 +++++++++++++++++++++++++----------- validator/assertion.go | 28 +++++++++------------ validator/builder_backend.go | 8 +----- validator/l1_validator.go | 25 +++++++++++++------ 4 files changed, 65 insertions(+), 44 deletions(-) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index e1f7722511..cf31ba98fa 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -11,24 +11,38 @@ package arbtest import ( "context" "math/big" + "sync/atomic" "testing" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/offchainlabs/arbstate/validator" ) func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) { - ctx, cancelCtx := context.WithCancel(context.Background()) - defer cancelCtx() - l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) + ctx := context.Background() + broadcasterPort := 9644 + nodeAConf := arbnode.NodeConfigL1Test + if !createNodesFlaky && !stakeLatestFlaky { + nodeAConf.Broadcaster = true + nodeAConf.BroadcasterConfig = *newBroadcasterConfigTest(broadcasterPort) + } + l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1WithConfig(t, ctx, true, &nodeAConf) defer l1stack.Close() - l2clientB, l2nodeB := Create2ndNode(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, false) + nodeBConf := arbnode.NodeConfigL1Test + nodeBConf.BatchPoster = false + nodeBConf.DelayedSequencerConfig.FinalizeDistance = big.NewInt(1000000) + if !createNodesFlaky && !stakeLatestFlaky { + nodeBConf.BroadcastClient = true + nodeBConf.BroadcastClientConfig = *newBroadcastClientConfigTest(broadcasterPort) + } + l2clientB, l2nodeB := Create2ndNodeWithConfig(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, &nodeBConf) deployAuth := l1info.GetDefaultTransactOpts("RollupOwner") @@ -113,19 +127,24 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) ) Require(t, err) - // Continually make L2 transactions in a background thread l2info.GenerateAccount("BackgroundUser") tx = l2info.PrepareTx("Faucet", "BackgroundUser", l2info.TransferGas, balance, nil) err = l2clientA.SendTransaction(ctx, tx) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) - err = l2clientB.SendTransaction(ctx, tx) - Require(t, err) - _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) - Require(t, err) + if createNodesFlaky || stakeLatestFlaky { + err = l2clientB.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) + Require(t, err) + } + + // Continually make L2 transactions in a background thread + var testEnded int32 + defer (func() { atomic.StoreInt32(&testEnded, 1) })() go (func() { - for i := uint64(0); ctx.Err() == nil; i++ { + for i := uint64(0); atomic.LoadInt32(&testEnded) == 0; i++ { l2info.Accounts["BackgroundUser"].Nonce = i tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) err := l2clientA.SendTransaction(ctx, tx) @@ -133,13 +152,14 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) if createNodesFlaky || stakeLatestFlaky { + // Create a different transaction for the second node l2info.Accounts["BackgroundUser"].Nonce = i tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) + err = l2clientB.SendTransaction(ctx, tx) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) + Require(t, err) } - err = l2clientB.SendTransaction(ctx, tx) - Require(t, err) - _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) - Require(t, err) } })() diff --git a/validator/assertion.go b/validator/assertion.go index 7e01c58a69..31225e1e81 100644 --- a/validator/assertion.go +++ b/validator/assertion.go @@ -80,34 +80,30 @@ func (a *Assertion) AsSolidityStruct() rollupgen.RollupLibAssertion { } } -func (a *Assertion) BeforeExecutionHash() common.Hash { - return a.BeforeState.BlockStateHash() -} - -func (a *Assertion) AfterExecutionHash() common.Hash { - return a.AfterState.BlockStateHash() -} - -func BisectionChunkHash( +func HashChallengeState( segmentStart uint64, segmentLength uint64, - startHash common.Hash, - endHash common.Hash, + hashes []common.Hash, ) common.Hash { + var hashesBytes []byte + for _, h := range hashes { + hashesBytes = append(hashesBytes, h[:]...) + } return crypto.Keccak256Hash( math.U256Bytes(new(big.Int).SetUint64(segmentStart)), math.U256Bytes(new(big.Int).SetUint64(segmentLength)), - startHash[:], - endHash[:], + hashesBytes, ) } func (a *Assertion) ExecutionHash() common.Hash { - return BisectionChunkHash( + return HashChallengeState( 0, a.NumBlocks, - a.BeforeExecutionHash(), - a.AfterExecutionHash(), + []common.Hash{ + a.BeforeState.BlockStateHash(), + a.AfterState.BlockStateHash(), + }, ) } diff --git a/validator/builder_backend.go b/validator/builder_backend.go index 8e55ab7c30..f0f49fe762 100644 --- a/validator/builder_backend.go +++ b/validator/builder_backend.go @@ -52,13 +52,7 @@ func (b *BuilderBackend) ClearTransactions() { } func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { - if call.From == b.builderAuth.From { - if b.wallet.Address() == nil { - return 0, nil - } - call.From = *b.wallet.Address() - } - return b.EstimateGas(ctx, call) + return 0, nil } func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { diff --git a/validator/l1_validator.go b/validator/l1_validator.go index e83f1eb362..8bd3fe2f24 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -382,8 +382,9 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake // we haven't validated any new blocks return nil, wrongNodesExist, nil } - var assertingBatchNumber uint64 - var assertingPosInBatch uint64 + var assertionCoversBatch uint64 + var afterGsBatch uint64 + var afterGsPosInBatch uint64 for i := localBatchCount - 1; i+1 >= minBatchCount && i > 0; i-- { lastBlockNum, err := v.inboxTracker.GetBatchMessageCount(i) if err != nil { @@ -401,12 +402,22 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake // We haven't reached the minimum assertion size yet break } - assertingBatchNumber = i - assertingPosInBatch = lastBlockValidated - prevBlockNum - 1 + assertionCoversBatch = i + if lastBlockValidated < lastBlockNum { + afterGsBatch = i + afterGsPosInBatch = lastBlockValidated - prevBlockNum + } else { + afterGsBatch = i + 1 + afterGsPosInBatch = 0 + } break } } - validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(assertingBatchNumber) + if assertionCoversBatch == 0 { + // we haven't validated the next batch completely + return nil, wrongNodesExist, nil + } + validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(assertionCoversBatch) if err != nil { return nil, false, err } @@ -441,8 +452,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake GlobalState: GoGlobalState{ BlockHash: assertingBlock.Hash(), SendRoot: assertingBlockExtra.SendRoot, - Batch: assertingBatchNumber, - PosInBatch: assertingPosInBatch, + Batch: afterGsBatch, + PosInBatch: afterGsPosInBatch, }, MachineStatus: MachineStatusFinished, }, From 6d381954be841105b6eb5c3538273420b798fee6 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 17:51:37 -0600 Subject: [PATCH 012/110] Augment test checks --- system_tests/staker_test.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index cf31ba98fa..7ff68e3b3f 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -177,10 +177,35 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") } - for j := 0; j < 20; j++ { + for j := 0; j < 10; j++ { TransferBalance(t, "Faucet", "Faucet", common.Big0, l1info, l1client, ctx) } } + + latestConfirmedNode, err := rollup.LatestConfirmed(&bind.CallOpts{}) + Require(t, err) + + if latestConfirmedNode <= 1 { + latestCreatedNode, err := rollup.LatestNodeCreated(&bind.CallOpts{}) + Require(t, err) + t.Fatal("latest confirmed node didn't advance:", latestConfirmedNode, latestCreatedNode) + } + + if !createNodesFlaky { + isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrA) + Require(t, err) + if !isStaked { + t.Fatal("staker A isn't staked") + } + } + + if !stakeLatestFlaky { + isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrB) + Require(t, err) + if !isStaked { + t.Fatal("staker B isn't staked") + } + } } func TestStakersCooperative(t *testing.T) { From 75f0fe31c5e7b4a81ecd69af2ef87f298f2cdb02 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 18:00:36 -0600 Subject: [PATCH 013/110] Improve logging --- .gitignore | 1 + broadcaster/broadcaster.go | 10 +++++----- validator/l1_validator.go | 12 +++++++++++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 2996df3730..57ec4cf39a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /replay /cmd/replay/replay /*.bin +/*.test .idea .vscode cmd/node/data diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index fc697baaa6..5e90a000b0 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -130,11 +130,11 @@ func (b *SequenceNumberCatchupBuffer) OnDoBroadcast(bmi interface{}) error { } else if expectedSequenceNumber := b.messages[len(b.messages)-1].SequenceNumber + 1; newMsg.SequenceNumber == expectedSequenceNumber { b.messages = append(b.messages, newMsg) } else if newMsg.SequenceNumber > expectedSequenceNumber { - log.Warn("Message with sequence number ", - newMsg.SequenceNumber, " requested to be broadcast, ", - "expected ", expectedSequenceNumber, - ", discarding up to ", newMsg.SequenceNumber, - " from catchup buffer") + log.Warn( + "Message requested to be broadcast has unexpected sequence number; discarding to seqNum from catchup buffer", + "seqNum", newMsg.SequenceNumber, + "expectedSeqNum", expectedSequenceNumber, + ) b.messages = nil b.messages = append(b.messages, newMsg) } else { diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 8bd3fe2f24..a218b21781 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -351,7 +351,17 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } continue } else { - log.Warn("found node with incorrect assertion", "node", nd.NodeNum) + log.Warn( + "found node with incorrect assertion", + "node", nd.NodeNum, + "inboxPositionInvalid", inboxPositionInvalid, + "numBlocks", nd.Assertion.NumBlocks, + "expectedNumBlocks", expectedNumBlocks, + "blockHash", afterGs.BlockHash, + "expectedBlockHash", lastBlock.Hash(), + "sendRoot", afterGs.SendRoot, + "expectedSendRoot", lastBlockExtra.SendRoot, + ) } } else { log.Warn("found younger sibling to correct node", "node", nd.NodeNum) From be9eff412b6627a2178f6611a10a82e95b1eb906 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 22:04:08 -0600 Subject: [PATCH 014/110] Add check for genesis block equality --- system_tests/staker_test.go | 6 ++++++ validator/l1_validator.go | 1 + 2 files changed, 7 insertions(+) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 7ff68e3b3f..d16eaeb432 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -44,6 +44,12 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) } l2clientB, l2nodeB := Create2ndNodeWithConfig(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, &nodeBConf) + nodeAGenesis := l2nodeA.Backend.APIBackend().CurrentHeader().Hash() + nodeBGenesis := l2nodeB.Backend.APIBackend().CurrentHeader().Hash() + if nodeAGenesis != nodeBGenesis { + t.Fatal("node A L2 genesis hash", nodeAGenesis, "!= node B L2 genesis hash", nodeBGenesis) + } + deployAuth := l1info.GetDefaultTransactOpts("RollupOwner") valWalletFactory, tx, _, err := rollupgen.DeployValidatorWalletCreator(&deployAuth, l1client) diff --git a/validator/l1_validator.go b/validator/l1_validator.go index a218b21781..d1a707cf04 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -355,6 +355,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake "found node with incorrect assertion", "node", nd.NodeNum, "inboxPositionInvalid", inboxPositionInvalid, + "computedBlockNum", lastBlockNum, "numBlocks", nd.Assertion.NumBlocks, "expectedNumBlocks", expectedNumBlocks, "blockHash", afterGs.BlockHash, From 07d18c32883dbf9680540c9fe2cc414b0a9549ec Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 16 Feb 2022 22:10:49 -0600 Subject: [PATCH 015/110] Fix full challenge test and prepare for staker challenge tests --- arbstate/inbox.go | 36 +++++++++++-------- .../src/challenge/BlockChallengeFactory.sol | 1 + .../challenge/ExecutionChallengeFactory.sol | 1 + .../src/challenge/IBlockChallengeFactory.sol | 2 ++ .../challenge/IExecutionChallengeFactory.sol | 2 ++ solgen/src/mocks/SequencerInboxStub.sol | 14 ++++++++ system_tests/full_challenge_test.go | 23 +++++++----- system_tests/staker_challenge_test.go | 19 ++++++++++ system_tests/staker_test.go | 16 ++++----- 9 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 system_tests/staker_challenge_test.go diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 79c4d948f3..4c338c0750 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -63,22 +63,24 @@ func parseSequencerMessage(data []byte) *sequencerMessage { maxL1Block := binary.BigEndian.Uint64(data[24:32]) afterDelayedMessages := binary.BigEndian.Uint64(data[32:40]) var segments [][]byte - if len(data) >= 41 && data[40] == 0 { - reader := io.LimitReader(brotli.NewReader(bytes.NewReader(data[41:])), maxDecompressedLen) - stream := rlp.NewStream(reader, uint64(maxDecompressedLen)) - for { - var segment []byte - err := stream.Decode(&segment) - if err != nil { - if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) { - log.Warn("error parsing sequencer message segment", "err", err.Error()) + if len(data) >= 41 { + if data[40] == 0 { + reader := io.LimitReader(brotli.NewReader(bytes.NewReader(data[41:])), maxDecompressedLen) + stream := rlp.NewStream(reader, uint64(maxDecompressedLen)) + for { + var segment []byte + err := stream.Decode(&segment) + if err != nil { + if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) { + log.Warn("error parsing sequencer message segment", "err", err.Error()) + } + break } - break + segments = append(segments, segment) } - segments = append(segments, segment) + } else { + log.Warn("unknown sequencer message format") } - } else { - log.Warn("unknown sequencer message format") } return &sequencerMessage{ minTimestamp: minTimestamp, @@ -302,7 +304,13 @@ func (r *inboxMultiplexer) getNextMsg() (*MessageWithMetadata, error) { } } else if segmentKind == BatchSegmentKindDelayedMessages { if r.delayedMessagesRead >= seqMsg.afterDelayedMessages { - log.Warn("attempt to access delayed msg", "msg", r.delayedMessagesRead, "segment_upto", seqMsg.afterDelayedMessages) + if segmentNum < uint64(len(seqMsg.segments)) { + log.Warn( + "attempt to read past batch delayed message count", + "delayedMessagesRead", r.delayedMessagesRead, + "batchAfterDelayedMessages", seqMsg.afterDelayedMessages, + ) + } msg = &MessageWithMetadata{ Message: invalidMessage, DelayedMessagesRead: seqMsg.afterDelayedMessages, diff --git a/solgen/src/challenge/BlockChallengeFactory.sol b/solgen/src/challenge/BlockChallengeFactory.sol index 67197acf7e..5d1c353206 100644 --- a/solgen/src/challenge/BlockChallengeFactory.sol +++ b/solgen/src/challenge/BlockChallengeFactory.sol @@ -42,6 +42,7 @@ contract BlockChallengeFactory is IBlockChallengeFactory { asserterTimeLeft_, challengerTimeLeft_ ); + emit ChallengeCreated(IChallenge(clone)); return IChallenge(clone); } } diff --git a/solgen/src/challenge/ExecutionChallengeFactory.sol b/solgen/src/challenge/ExecutionChallengeFactory.sol index dfcdfc019f..e07786d75d 100644 --- a/solgen/src/challenge/ExecutionChallengeFactory.sol +++ b/solgen/src/challenge/ExecutionChallengeFactory.sol @@ -39,6 +39,7 @@ contract ExecutionChallengeFactory is IExecutionChallengeFactory { asserterTimeLeft_, challengerTimeLeft_ ); + emit ChallengeCreated(IChallenge(clone)); return IChallenge(clone); } } diff --git a/solgen/src/challenge/IBlockChallengeFactory.sol b/solgen/src/challenge/IBlockChallengeFactory.sol index 6baec1edcd..cdd3f5d21e 100644 --- a/solgen/src/challenge/IBlockChallengeFactory.sol +++ b/solgen/src/challenge/IBlockChallengeFactory.sol @@ -6,6 +6,8 @@ import "./IChallenge.sol"; import "./IChallengeResultReceiver.sol"; interface IBlockChallengeFactory { + event ChallengeCreated(IChallenge challenge); + // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function createChallenge( address[3] calldata contractAddresses, diff --git a/solgen/src/challenge/IExecutionChallengeFactory.sol b/solgen/src/challenge/IExecutionChallengeFactory.sol index d86e790c1b..ecf4962d13 100644 --- a/solgen/src/challenge/IExecutionChallengeFactory.sol +++ b/solgen/src/challenge/IExecutionChallengeFactory.sol @@ -6,6 +6,8 @@ import "./IChallenge.sol"; import "./IChallengeResultReceiver.sol"; interface IExecutionChallengeFactory { + event ChallengeCreated(IChallenge challenge); + function createChallenge( IChallengeResultReceiver resultReceiver_, ExecutionContext memory execCtx_, diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index 0d2546a337..054d459346 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -40,6 +40,20 @@ contract SequencerInboxStub is ISequencerInbox { constructor(IBridge _delayedBridge, address _sequencer) { delayedBridge = _delayedBridge; isBatchPoster[_sequencer] = true; + + bytes memory header = abi.encodePacked( + uint64(0), + uint64(0), + uint64(0), + uint64(0), + uint64(0) + ); + bytes32 headerHash = keccak256(header); + bytes32 acc = keccak256(abi.encodePacked(bytes32(0), headerHash, bytes32(0))); + inboxAccs.push(acc); + bytes memory data; + uint64[4] memory timeBounds; + emit SequencerBatchDelivered(0, bytes32(0), acc, bytes32(0), 0, timeBounds, data); } function addSequencerL2BatchFromOrigin( diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 3ca522ddc9..2df27e9f2b 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -27,6 +27,7 @@ import ( "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/offchainlabs/arbstate/solgen/go/mocksgen" "github.com/offchainlabs/arbstate/solgen/go/ospgen" @@ -89,15 +90,17 @@ func CreateChallenge( t.Fatal(err) } - challengeAddr, tx, challenge, err := challengegen.DeployBlockChallenge(auth, client) + _, tx, blockChallengeFactory, err := challengegen.DeployBlockChallengeFactory(auth, client, executionChallengeFactoryAddr) + if err != nil { + t.Fatal(err) + } _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } - tx, err = challenge.Initialize( + tx, err = blockChallengeFactory.CreateChallenge( auth, - executionChallengeFactoryAddr, [3]common.Address{ resultReceiverAddr, sequencerInbox, @@ -118,15 +121,17 @@ func CreateChallenge( big.NewInt(100000), big.NewInt(100000), ) + receipt, err := arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } - _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) + + challengeCreatedEvent, err := blockChallengeFactory.ParseChallengeCreated(*receipt.Logs[len(receipt.Logs)-1]) if err != nil { t.Fatal(err) } - return resultReceiver, challengeAddr + return resultReceiver, challengeCreatedEvent.Challenge } func writeTxToBatch(writer io.Writer, tx *types.Transaction) error { @@ -162,7 +167,7 @@ func makeBatch(t *testing.T, l2Node *arbnode.Node, l2Info *BlockchainTestInfo, b t.Fatal(err) } - tx, err := seqInbox.AddSequencerL2BatchFromOrigin(sequencer, big.NewInt(0), batchBuffer.Bytes(), big.NewInt(0), common.Address{}) + tx, err := seqInbox.AddSequencerL2BatchFromOrigin(sequencer, big.NewInt(1), batchBuffer.Bytes(), big.NewInt(0), common.Address{}) if err != nil { t.Fatal(err) } @@ -273,7 +278,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { trueSeqInboxAddr = asserterSeqInboxAddr expectedWinner = l1Info.GetAddress("asserter") } - ospEntry := DeployOneStepProofEntry(t, &deployerTxOpts, l1Backend, delayedBridge, trueSeqInboxAddr) + ospEntry := DeployOneStepProofEntry(t, &deployerTxOpts, l1Backend) wasmModuleRoot, err := validator.GetInitialModuleRoot(ctx) if err != nil { @@ -298,7 +303,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { } asserterEndGlobalState := validator.GoGlobalState{ BlockHash: asserterLatestBlock.Hash(), - Batch: 1, + Batch: 2, PosInBatch: 0, } numBlocks := asserterLatestBlock.NumberU64() - asserterGenesis.NumberU64() @@ -354,7 +359,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { t.Log("challenge completed! asserter hit expected error:", err) return } - t.Fatal(err) + t.Fatal("challenge step", i, "hit error:", err) } if tx == nil { t.Fatal("no move") diff --git a/system_tests/staker_challenge_test.go b/system_tests/staker_challenge_test.go new file mode 100644 index 0000000000..a4374a2398 --- /dev/null +++ b/system_tests/staker_challenge_test.go @@ -0,0 +1,19 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +// race detection makes things slow and miss timeouts +//go:build fullchallengetest +// +build fullchallengetest + +package arbtest + +import "testing" + +func TestStakersMakeNodesFaulty(t *testing.T) { + stakerTestImpl(t, true, false) +} + +func TestStakersStakeLatestFaulty(t *testing.T) { + stakerTestImpl(t, false, true) +} diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index d16eaeb432..d301fcbe6b 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -24,11 +24,11 @@ import ( "github.com/offchainlabs/arbstate/validator" ) -func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) { +func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) { ctx := context.Background() broadcasterPort := 9644 nodeAConf := arbnode.NodeConfigL1Test - if !createNodesFlaky && !stakeLatestFlaky { + if !makeNodesFaulty && !stakeLatestFaulty { nodeAConf.Broadcaster = true nodeAConf.BroadcasterConfig = *newBroadcasterConfigTest(broadcasterPort) } @@ -38,7 +38,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) nodeBConf := arbnode.NodeConfigL1Test nodeBConf.BatchPoster = false nodeBConf.DelayedSequencerConfig.FinalizeDistance = big.NewInt(1000000) - if !createNodesFlaky && !stakeLatestFlaky { + if !makeNodesFaulty && !stakeLatestFaulty { nodeBConf.BroadcastClient = true nodeBConf.BroadcastClientConfig = *newBroadcastClientConfigTest(broadcasterPort) } @@ -121,7 +121,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) l1client, valWalletB, 0, - validator.MakeNodesStrategy, + validator.StakeLatestStrategy, bind.CallOpts{}, &l1authB, valConfig, @@ -139,7 +139,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) - if createNodesFlaky || stakeLatestFlaky { + if makeNodesFaulty || stakeLatestFaulty { err = l2clientB.SendTransaction(ctx, tx) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) @@ -157,7 +157,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) - if createNodesFlaky || stakeLatestFlaky { + if makeNodesFaulty || stakeLatestFaulty { // Create a different transaction for the second node l2info.Accounts["BackgroundUser"].Nonce = i tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) @@ -197,7 +197,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) t.Fatal("latest confirmed node didn't advance:", latestConfirmedNode, latestCreatedNode) } - if !createNodesFlaky { + if !makeNodesFaulty { isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrA) Require(t, err) if !isStaked { @@ -205,7 +205,7 @@ func stakerTestImpl(t *testing.T, createNodesFlaky bool, stakeLatestFlaky bool) } } - if !stakeLatestFlaky { + if !stakeLatestFaulty { isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrB) Require(t, err) if !isStaked { From ce9d17f058fb69f34c3cbc54a0aa85c885bddeea Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 10:44:11 -0600 Subject: [PATCH 016/110] Ignore broadcast feed for validator --- system_tests/staker_test.go | 30 ++++++++++++++---------------- validator/l1_validator.go | 17 +++++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index d301fcbe6b..88b8d8bf26 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -18,7 +18,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" - "github.com/offchainlabs/arbstate/arbnode" "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/offchainlabs/arbstate/validator" @@ -26,23 +25,10 @@ import ( func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) { ctx := context.Background() - broadcasterPort := 9644 - nodeAConf := arbnode.NodeConfigL1Test - if !makeNodesFaulty && !stakeLatestFaulty { - nodeAConf.Broadcaster = true - nodeAConf.BroadcasterConfig = *newBroadcasterConfigTest(broadcasterPort) - } - l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1WithConfig(t, ctx, true, &nodeAConf) + l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) defer l1stack.Close() - nodeBConf := arbnode.NodeConfigL1Test - nodeBConf.BatchPoster = false - nodeBConf.DelayedSequencerConfig.FinalizeDistance = big.NewInt(1000000) - if !makeNodesFaulty && !stakeLatestFaulty { - nodeBConf.BroadcastClient = true - nodeBConf.BroadcastClientConfig = *newBroadcastClientConfigTest(broadcasterPort) - } - l2clientB, l2nodeB := Create2ndNodeWithConfig(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, &nodeBConf) + l2clientB, l2nodeB := Create2ndNode(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, false) nodeAGenesis := l2nodeA.Backend.APIBackend().CurrentHeader().Hash() nodeBGenesis := l2nodeB.Backend.APIBackend().CurrentHeader().Hash() @@ -169,14 +155,22 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) } })() + stakerATxs := 0 + stakerBTxs := 0 for i := 0; i < 100; i++ { var stakerName string if i%2 == 0 { stakerName = "A" tx, err = stakerA.Act(ctx) + if tx != nil { + stakerATxs++ + } } else { stakerName = "B" tx, err = stakerB.Act(ctx) + if tx != nil { + stakerBTxs++ + } } Require(t, err, "Staker", stakerName, "failed to act") if tx != nil { @@ -188,6 +182,10 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) } } + if stakerATxs == 0 || stakerBTxs == 0 { + t.Fatal("staker didn't make txs: staker A made", stakerATxs, "staker B made", stakerBTxs) + } + latestConfirmedNode, err := rollup.LatestConfirmed(&bind.CallOpts{}) Require(t, err) diff --git a/validator/l1_validator.go b/validator/l1_validator.go index d1a707cf04..e1a4269da5 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -281,6 +281,23 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake blocksValidated = v.blockValidator.BlocksValidated() } else { blocksValidated = v.l2Blockchain.CurrentHeader().Number.Uint64() + + batchCount, err := v.inboxTracker.GetBatchCount() + if err != nil { + return nil, false, err + } + if batchCount > 0 { + messageCount, err := v.inboxTracker.GetBatchMessageCount(batchCount - 1) + if err != nil { + return nil, false, err + } + lastBatchBlock := v.genesisBlockNumber + messageCount + if blocksValidated > lastBatchBlock { + blocksValidated = lastBatchBlock + } + } else { + blocksValidated = 0 + } } currentL1Block, err := v.client.BlockByNumber(ctx, nil) From 612654825ffbb74463fb4880252a44c1f407c644 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 10:47:48 -0600 Subject: [PATCH 017/110] Set genesis block timestamp to 0 for now --- arbnode/node.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/node.go b/arbnode/node.go index 4f6807ad84..216e162b31 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -459,7 +459,7 @@ func WriteOrTestGenblock(chainDb ethdb.Database, initData *statetransfer.ArbosIn head := &types.Header{ Number: new(big.Int).SetUint64(blockNumber), Nonce: types.EncodeNonce(0), - Time: uint64(time.Now().Unix()), + Time: 0, ParentHash: prevHash, Extra: []byte("ArbitrumMainnet"), GasLimit: l2pricing.L2GasLimit, From c92cf53592e83df6f7bca61f404d1caca2899062 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 11:17:56 -0600 Subject: [PATCH 018/110] Compare local seq batch acc to assertion in validator --- validator/l1_validator.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/validator/l1_validator.go b/validator/l1_validator.go index e1a4269da5..2047fed6c5 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -282,12 +282,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } else { blocksValidated = v.l2Blockchain.CurrentHeader().Number.Uint64() - batchCount, err := v.inboxTracker.GetBatchCount() - if err != nil { - return nil, false, err - } - if batchCount > 0 { - messageCount, err := v.inboxTracker.GetBatchMessageCount(batchCount - 1) + if localBatchCount > 0 { + messageCount, err := v.inboxTracker.GetBatchMessageCount(localBatchCount - 1) if err != nil { return nil, false, err } @@ -334,12 +330,25 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } if correctNode == nil { afterGs := nd.AfterState().GlobalState + requiredBatches := nd.AfterState().RequiredBatches() + if localBatchCount < requiredBatches { + return nil, false, fmt.Errorf("waiting for validator to catch up to assertion batches: %v/%v", localBatchCount, requiredBatches) + } + if requiredBatches > 0 { + haveAcc, err := v.inboxTracker.GetBatchAcc(requiredBatches - 1) + if err != nil { + return nil, false, err + } + if haveAcc != nd.AfterInboxBatchAcc { + return nil, false, fmt.Errorf("missed sequencer batches reorg: at seq num %v have acc %v but assertion has acc %v", requiredBatches-1, haveAcc, nd.AfterInboxBatchAcc) + } + } lastBlockNum, inboxPositionInvalid, err := v.blockNumberFromGlobalState(afterGs) if err != nil { return nil, false, err } if blocksValidated < lastBlockNum { - return nil, false, fmt.Errorf("waiting for validator to catch up to assertion: %v/%v", blocksValidated, lastBlockNum) + return nil, false, fmt.Errorf("waiting for validator to catch up to assertion blocks: %v/%v", blocksValidated, lastBlockNum) } lastBlock := v.l2Blockchain.GetBlockByNumber(lastBlockNum) if lastBlock == nil { @@ -361,7 +370,12 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake afterGs.BlockHash == lastBlock.Hash() && afterGs.SendRoot == lastBlockExtra.SendRoot if valid { - log.Info("found correct node", "node", nd.NodeNum) + log.Info( + "found correct node", + "node", nd.NodeNum, + "blockNum", lastBlockNum, + "blockHash", afterGs.BlockHash, + ) correctNode = existingNodeAction{ number: nd.NodeNum, hash: nd.NodeHash, From 0b1bd6a773cfc8c63d51f83ecb713c49c9813091 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 11:28:30 -0600 Subject: [PATCH 019/110] Setup staker challenge test --- system_tests/staker_challenge_test.go | 6 ++-- system_tests/staker_test.go | 48 ++++++++++++++++++++------- validator/builder_backend.go | 2 +- 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/system_tests/staker_challenge_test.go b/system_tests/staker_challenge_test.go index a4374a2398..880eef380f 100644 --- a/system_tests/staker_challenge_test.go +++ b/system_tests/staker_challenge_test.go @@ -10,10 +10,10 @@ package arbtest import "testing" -func TestStakersMakeNodesFaulty(t *testing.T) { +func TestStakersFaultyHonestActive(t *testing.T) { stakerTestImpl(t, true, false) } -func TestStakersStakeLatestFaulty(t *testing.T) { - stakerTestImpl(t, false, true) +func TestStakersFaultyHonestInactive(t *testing.T) { + stakerTestImpl(t, true, true) } diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 88b8d8bf26..3a490bdd5d 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -10,6 +10,7 @@ package arbtest import ( "context" + "fmt" "math/big" "sync/atomic" "testing" @@ -23,17 +24,26 @@ import ( "github.com/offchainlabs/arbstate/validator" ) -func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) { +func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) { ctx := context.Background() l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) defer l1stack.Close() + if faultyStaker { + l2info.GenerateGenesysAccount("FaultyAddr", common.Big1) + } l2clientB, l2nodeB := Create2ndNode(t, ctx, l2nodeA, l1stack, &l2info.ArbInitData, false) nodeAGenesis := l2nodeA.Backend.APIBackend().CurrentHeader().Hash() nodeBGenesis := l2nodeB.Backend.APIBackend().CurrentHeader().Hash() - if nodeAGenesis != nodeBGenesis { - t.Fatal("node A L2 genesis hash", nodeAGenesis, "!= node B L2 genesis hash", nodeBGenesis) + if faultyStaker { + if nodeAGenesis == nodeBGenesis { + t.Fatal("node A L2 genesis hash", nodeAGenesis, "== node B L2 genesis hash", nodeBGenesis) + } + } else { + if nodeAGenesis != nodeBGenesis { + t.Fatal("node A L2 genesis hash", nodeAGenesis, "!= node B L2 genesis hash", nodeBGenesis) + } } deployAuth := l1info.GetDefaultTransactOpts("RollupOwner") @@ -125,7 +135,7 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) - if makeNodesFaulty || stakeLatestFaulty { + if faultyStaker { err = l2clientB.SendTransaction(ctx, tx) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) @@ -143,7 +153,7 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) Require(t, err) _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) Require(t, err) - if makeNodesFaulty || stakeLatestFaulty { + if faultyStaker { // Create a different transaction for the second node l2info.Accounts["BackgroundUser"].Nonce = i tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) @@ -157,16 +167,19 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) stakerATxs := 0 stakerBTxs := 0 + sawStakerZombie := false for i := 0; i < 100; i++ { var stakerName string if i%2 == 0 { stakerName = "A" + fmt.Printf("staker A acting:\n") tx, err = stakerA.Act(ctx) if tx != nil { stakerATxs++ } } else { stakerName = "B" + fmt.Printf("staker B acting:\n") tx, err = stakerB.Act(ctx) if tx != nil { stakerBTxs++ @@ -177,6 +190,15 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") } + if faultyStaker && !sawStakerZombie { + sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, valWalletAddrB) + Require(t, err) + } + isHonestZombie, err := rollup.IsZombie(&bind.CallOpts{}, valWalletAddrA) + Require(t, err) + if isHonestZombie { + t.Fatal("staker A became a zombie") + } for j := 0; j < 10; j++ { TransferBalance(t, "Faucet", "Faucet", common.Big0, l1info, l1client, ctx) } @@ -195,15 +217,17 @@ func stakerTestImpl(t *testing.T, makeNodesFaulty bool, stakeLatestFaulty bool) t.Fatal("latest confirmed node didn't advance:", latestConfirmedNode, latestCreatedNode) } - if !makeNodesFaulty { - isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrA) - Require(t, err) - if !isStaked { - t.Fatal("staker A isn't staked") - } + if faultyStaker && !sawStakerZombie { + t.Fatal("staker B didn't become a zombie despite being faulty") + } + + isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrA) + Require(t, err) + if !isStaked { + t.Fatal("staker A isn't staked") } - if !stakeLatestFaulty { + if !faultyStaker { isStaked, err := rollup.IsStaked(&bind.CallOpts{}, valWalletAddrB) Require(t, err) if !isStaked { diff --git a/validator/builder_backend.go b/validator/builder_backend.go index f0f49fe762..433b4f1bb8 100644 --- a/validator/builder_backend.go +++ b/validator/builder_backend.go @@ -71,7 +71,7 @@ func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transact Value: totalAmount, Data: realData, } - _, err = b.EstimateGas(ctx, msg) + _, err = b.L1Interface.PendingCallContract(ctx, msg) return errors.WithStack(err) } From 8ad394df335165afad3c0b07d22257a772847e09 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 12:42:38 -0600 Subject: [PATCH 020/110] Fix various staker challenge issues --- arbnode/node.go | 17 ++++- system_tests/full_challenge_test.go | 4 +- system_tests/staker_test.go | 13 +++- validator/block_challenge_backend.go | 67 +++++++++---------- validator/block_validator.go | 12 +++- validator/challenge_manager.go | 99 +++++++++++++++++----------- validator/staker.go | 3 +- 7 files changed, 129 insertions(+), 86 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 216e162b31..1e8ceba04a 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -31,6 +31,7 @@ import ( "github.com/offchainlabs/arbstate/broadcastclient" "github.com/offchainlabs/arbstate/broadcaster" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" + "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/offchainlabs/arbstate/solgen/go/ospgen" "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/offchainlabs/arbstate/statetransfer" @@ -134,7 +135,19 @@ func deployChallengeFactory(ctx context.Context, client arbutil.L1Interface, aut return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - return ospEntryAddr, nil + execChallengeFactoryAddr, tx, _, err := challengegen.DeployExecutionChallengeFactory(auth, client, ospEntryAddr) + err = andTxSucceeded(ctx, client, txTimeout, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + } + + blockChallengeFactoryAddr, tx, _, err := challengegen.DeployBlockChallengeFactory(auth, client, execChallengeFactoryAddr) + err = andTxSucceeded(ctx, client, txTimeout, tx, err) + if err != nil { + return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + } + + return blockChallengeFactoryAddr, nil } func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (*rollupgen.RollupCreator, error) { @@ -188,7 +201,7 @@ func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *b } var confirmPeriodBlocks uint64 = 20 - var extraChallengeTimeBlocks uint64 = 20 + var extraChallengeTimeBlocks uint64 = 200 seqInboxParams := rollupgen.ISequencerInboxMaxTimeVariation{ DelayBlocks: big.NewInt(60 * 60 * 24 * 15), FutureBlocks: big.NewInt(12), diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 2df27e9f2b..73a35379c0 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -324,12 +324,12 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { ) confirmLatestBlock(ctx, t, l1Info, l1Backend) - asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, challenge, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4) + asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challenge, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } - challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challenge, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4) + challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challenge, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 3a490bdd5d..5302ac279e 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -143,10 +143,10 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) } // Continually make L2 transactions in a background thread - var testEnded int32 - defer (func() { atomic.StoreInt32(&testEnded, 1) })() + var stopBackgroundTxs int32 + defer (func() { atomic.StoreInt32(&stopBackgroundTxs, 1) })() go (func() { - for i := uint64(0); atomic.LoadInt32(&testEnded) == 0; i++ { + for i := uint64(0); atomic.LoadInt32(&stopBackgroundTxs) == 0; i++ { l2info.Accounts["BackgroundUser"].Nonce = i tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) err := l2clientA.SendTransaction(ctx, tx) @@ -190,6 +190,13 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) Require(t, err, "EnsureTxSucceeded failed for staker", stakerName, "tx") } + if faultyStaker { + challengeAddr, err := rollup.CurrentChallenge(&bind.CallOpts{}, valWalletAddrA) + Require(t, err) + if challengeAddr != (common.Address{}) { + atomic.StoreInt32(&stopBackgroundTxs, 1) + } + } if faultyStaker && !sawStakerZombie { sawStakerZombie, err = rollup.IsZombie(&bind.CallOpts{}, valWalletAddrB) Require(t, err) diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index be616c6d68..2fb08f7ba0 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -64,7 +64,7 @@ type BlockChallengeBackend struct { blockChallengeCon *challengegen.BlockChallenge client bind.ContractBackend bc *core.BlockChain - startBlock uint64 + startBlock int64 startPosition uint64 endPosition uint64 startGs GoGlobalState @@ -76,7 +76,7 @@ type BlockChallengeBackend struct { // Assert that BlockChallengeBackend implements ChallengeBackend var _ ChallengeBackend = (*BlockChallengeBackend)(nil) -func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address) (*BlockChallengeBackend, error) { +func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNum uint64) (*BlockChallengeBackend, error) { callOpts := &bind.CallOpts{Context: ctx} challengeCon, err := challengegen.NewBlockChallenge(challengeAddr, client) if err != nil { @@ -91,11 +91,14 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra if startGs.PosInBatch != 0 { return nil, errors.New("challenge started misaligned with batch boundary") } - startBlock := bc.GetBlockByHash(startGs.BlockHash) - if startBlock == nil { - return nil, errors.New("failed to find start block") + startBlockNum := int64(genesisBlockNum) - 1 + if startGs.BlockHash != (common.Hash{}) { + startBlock := bc.GetBlockByHash(startGs.BlockHash) + if startBlock == nil { + return nil, errors.New("failed to find start block") + } + startBlockNum = int64(startBlock.NumberU64()) } - startBlockNum := startBlock.NumberU64() var startMsgCount uint64 if startGs.Batch > 0 { @@ -104,7 +107,11 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, errors.Wrap(err, "failed to get challenge start batch metadata") } } - if startMsgCount != startBlockNum { + var expectedMsgCount uint64 + if startBlockNum > 0 { + expectedMsgCount = uint64(startBlockNum) - genesisBlockNum + } + if startMsgCount != expectedMsgCount { return nil, errors.New("start block and start message count are not 1:1") } @@ -119,15 +126,10 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra if endGs.Batch <= startGs.Batch { return nil, errors.New("challenge didn't advance batch") } - lastMsgCount, err := inboxTracker.GetBatchMessageCount(endGs.Batch - 1) + endMsgCount, err := inboxTracker.GetBatchMessageCount(endGs.Batch - 1) if err != nil { return nil, errors.Wrap(err, "failed to get challenge end batch metadata") } - endMsgCount := lastMsgCount - endBatchBlock := bc.GetBlockByNumber(endMsgCount) - if endBatchBlock == nil { - return nil, errors.New("missing block at end of last challenge batch") - } return &BlockChallengeBackend{ client: client, @@ -139,7 +141,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra endPosition: math.MaxUint64, endGs: endGs, inboxTracker: inboxTracker, - tooFarStartsAtPosition: endMsgCount - startBlockNum + 1, + tooFarStartsAtPosition: endMsgCount - startMsgCount + 1, }, nil } @@ -202,15 +204,20 @@ func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, h const STATUS_FINISHED uint8 = 1 const STATUS_TOO_FAR uint8 = 3 -func (b *BlockChallengeBackend) GetBlockNrAtStep(step uint64) uint64 { - return b.startBlock + step +func (b *BlockChallengeBackend) GetBlockNrAtStep(step uint64) int64 { + return b.startBlock + int64(step) } func (b *BlockChallengeBackend) GetInfoAtStep(ctx context.Context, step uint64) (GoGlobalState, uint8, error) { if step >= b.tooFarStartsAtPosition { return GoGlobalState{}, STATUS_TOO_FAR, nil } - header := b.bc.GetHeaderByNumber(b.GetBlockNrAtStep(step)) + blockNum := b.GetBlockNrAtStep(step) + if blockNum == -1 { + // Pre-genesis state + return GoGlobalState{}, STATUS_FINISHED, nil + } + header := b.bc.GetHeaderByNumber(uint64(blockNum)) if header == nil { return GoGlobalState{}, 0, errors.New("failed to get block in block challenge") } @@ -288,16 +295,14 @@ func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client b ) } -func inExecChallengeError(err error) (bool, common.Address, uint64, error) { +func inExecChallengeError(err error) (bool, common.Address, int64, error) { return false, common.Address{}, 0, err } -func (b *BlockChallengeBackend) IsInExecutionChallenge(ctx context.Context) (bool, common.Address, uint64, error) { - callOpts := &bind.CallOpts{Context: ctx} - var err error - callOpts.BlockNumber, err = LatestConfirmedBlock(ctx, b.client) - if err != nil { - return inExecChallengeError(err) +func (b *BlockChallengeBackend) IsInExecutionChallenge(ctx context.Context, latestConfirmedBlock *big.Int) (bool, common.Address, int64, error) { + callOpts := &bind.CallOpts{ + Context: ctx, + BlockNumber: latestConfirmedBlock, } addr, err := b.blockChallengeCon.ExecutionChallenge(callOpts) if err != nil { @@ -307,21 +312,9 @@ func (b *BlockChallengeBackend) IsInExecutionChallenge(ctx context.Context) (boo return inExecChallengeError(nil) } - startGs, err := b.blockChallengeCon.GetStartGlobalState(callOpts) - if err != nil { - return inExecChallengeError(err) - } - startHeader := b.bc.GetHeaderByHash(GoGlobalStateFromSolidity(startGs).BlockHash) - if startHeader == nil { - return inExecChallengeError(errors.New("failed to find challenge start block")) - } blockOffset, err := b.blockChallengeCon.ExecutionChallengeAtSteps(callOpts) if err != nil { return inExecChallengeError(err) } - blockNumber := new(big.Int).Add(startHeader.Number, blockOffset) - if !blockNumber.IsUint64() { - return inExecChallengeError(errors.New("execution challenge occurred at non-uint64 block number")) - } - return true, addr, blockNumber.Uint64(), nil + return true, addr, b.startBlock + blockOffset.Int64(), nil } diff --git a/validator/block_validator.go b/validator/block_validator.go index 90b7834d0b..dfe5739f38 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -192,7 +192,11 @@ func RecordBlockCreation(blockchain *core.BlockChain, prevHeader *types.Header, } func BlockDataForValidation(blockchain *core.BlockChain, header, prevHeader *types.Header, msg arbstate.MessageWithMetadata) (preimages map[common.Hash][]byte, hasDelayedMessage bool, delayedMsgNr uint64, err error) { - if header.ParentHash != prevHeader.Hash() { + var prevHash common.Hash + if prevHeader != nil { + prevHash = prevHeader.Hash() + } + if header.ParentHash != prevHash { err = fmt.Errorf("bad arguments: prev does not match") return } @@ -206,9 +210,11 @@ func BlockDataForValidation(blockchain *core.BlockChain, header, prevHeader *typ err = fmt.Errorf("wrong hash expected %s got %s", header.Hash(), blockhash) return } - if header.Nonce != prevHeader.Nonce { + if prevHeader == nil || header.Nonce != prevHeader.Nonce { hasDelayedMessage = true - delayedMsgNr = prevHeader.Nonce.Uint64() + if prevHeader != nil { + delayedMsgNr = prevHeader.Nonce.Uint64() + } } return } diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index a65cfd0d80..e83d64a868 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -6,6 +6,7 @@ package validator import ( "context" + "fmt" "math/big" "strings" @@ -21,8 +22,7 @@ import ( "github.com/pkg/errors" ) -const MAX_BISECTION_DEGREE uint64 = 40 -const CONFIRMATION_BLOCKS int64 = 12 +const maxBisectionDegree uint64 = 40 var challengeBisectedID common.Hash @@ -34,23 +34,6 @@ func init() { challengeBisectedID = parsedChallengeCoreABI.Events["Bisected"].ID } -// Returns nil if client is a SimulatedBackend -func LatestConfirmedBlock(ctx context.Context, client bind.ContractBackend) (*big.Int, error) { - _, isSimulated := client.(*backends.SimulatedBackend) - if isSimulated { - return nil, nil - } - latestBlock, err := client.HeaderByNumber(ctx, nil) - if err != nil { - return nil, err - } - block := new(big.Int).Sub(latestBlock.Number, big.NewInt(CONFIRMATION_BLOCKS)) - if block.Sign() < 0 { - block.SetInt64(0) - } - return block, nil -} - type ChallengeBackend interface { SetRange(ctx context.Context, start uint64, end uint64) error GetHashAtStep(ctx context.Context, position uint64) (common.Hash, error) @@ -58,13 +41,14 @@ type ChallengeBackend interface { type ChallengeManager struct { // fields used in both block and execution challenge - con *challengegen.ChallengeCore - challengeAddr common.Address - rootChallengeAddr common.Address - client bind.ContractBackend - auth *bind.TransactOpts - actingAs common.Address - startL1Block *big.Int + con *challengegen.ChallengeCore + challengeAddr common.Address + rootChallengeAddr common.Address + client bind.ContractBackend + auth *bind.TransactOpts + actingAs common.Address + startL1Block *big.Int + confirmationBlocks int64 // fields below are used while working on block challenge blockChallengeBackend *BlockChallengeBackend @@ -77,18 +61,22 @@ type ChallengeManager struct { targetNumMachines int initialMachine *ArbitratorMachine - initialMachineBlockNr uint64 + initialMachineBlockNr int64 // nil untill working on execution challenge executionChallengeBackend *ExecutionChallengeBackend } -func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, blockChallengeAddr common.Address, l2blockChain *core.BlockChain, inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, startL1Block uint64, targetNumMachines int) (*ChallengeManager, error) { +func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, fromAddr common.Address, blockChallengeAddr common.Address, l2blockChain *core.BlockChain, inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, startL1Block uint64, targetNumMachines int, confirmationBlocks int64) (*ChallengeManager, error) { challengeCoreCon, err := challengegen.NewChallengeCore(blockChallengeAddr, l1client) if err != nil { return nil, err } - backend, err := NewBlockChallengeBackend(ctx, l2blockChain, inboxTracker, l1client, blockChallengeAddr) + genesisBlockNum, err := txStreamer.GetGenesisBlockNumber() + if err != nil { + return nil, err + } + backend, err := NewBlockChallengeBackend(ctx, l2blockChain, inboxTracker, l1client, blockChallengeAddr, genesisBlockNum) if err != nil { return nil, err } @@ -98,7 +86,7 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut rootChallengeAddr: blockChallengeAddr, client: l1client, auth: auth, - actingAs: auth.From, + actingAs: fromAddr, startL1Block: new(big.Int).SetUint64(startL1Block), blockChallengeBackend: backend, inboxReader: inboxReader, @@ -106,6 +94,7 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut txStreamer: txStreamer, blockchain: l2blockChain, targetNumMachines: targetNumMachines, + confirmationBlocks: confirmationBlocks, }, nil } @@ -143,6 +132,23 @@ type ChallengeState struct { RawSegments [][32]byte } +// Returns nil if client is a SimulatedBackend +func (m *ChallengeManager) latestConfirmedBlock(ctx context.Context) (*big.Int, error) { + _, isSimulated := m.client.(*backends.SimulatedBackend) + if isSimulated { + return nil, nil + } + latestBlock, err := m.client.HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + block := new(big.Int).Sub(latestBlock.Number, big.NewInt(m.confirmationBlocks)) + if block.Sign() < 0 { + block.SetInt64(0) + } + return block, nil +} + func (m *ChallengeManager) RootChallengeAddress() common.Address { return m.rootChallengeAddr } @@ -204,7 +210,7 @@ func (m *ChallengeManager) bisect(ctx context.Context, backend ChallengeBackend, if err != nil { return nil, err } - bisectionDegree := MAX_BISECTION_DEGREE + bisectionDegree := maxBisectionDegree if newChallengeLength < bisectionDegree { bisectionDegree = newChallengeLength } @@ -244,7 +250,7 @@ func (m *ChallengeManager) IsMyTurn(ctx context.Context) (bool, error) { return false, nil } // Perform future checks against the latest confirmed block - callOpts.BlockNumber, err = LatestConfirmedBlock(ctx, m.client) + callOpts.BlockNumber, err = m.latestConfirmedBlock(ctx) if err != nil { return false, err } @@ -261,7 +267,7 @@ func (m *ChallengeManager) IsMyTurn(ctx context.Context) (bool, error) { func (m *ChallengeManager) GetChallengeState(ctx context.Context) (*ChallengeState, error) { callOpts := &bind.CallOpts{Context: ctx} var err error - callOpts.BlockNumber, err = LatestConfirmedBlock(ctx, m.client) + callOpts.BlockNumber, err = m.latestConfirmedBlock(ctx) if err != nil { return nil, err } @@ -299,16 +305,22 @@ func (m *ChallengeManager) ScanChallengeState(ctx context.Context, backend Chall return 0, errors.Errorf("agreed with entire challenge (start step count %v and end step count %v)", state.Start.String(), state.End.String()) } -func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum uint64) error { +func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum int64) error { if m.initialMachine != nil && m.initialMachineBlockNr == blockNum { return nil } - blockHeader := m.blockchain.GetHeaderByNumber(blockNum) initialFrozenMachine, err := GetZeroStepMachine(ctx) if err != nil { return err } machine := initialFrozenMachine.Clone() + var blockHeader *types.Header + if blockNum != -1 { + blockHeader = m.blockchain.GetHeaderByNumber(uint64(blockNum)) + if blockHeader == nil { + return fmt.Errorf("block header %v before challenge point unknown", blockNum) + } + } globalState, err := m.blockChallengeBackend.FindGlobalStateFromHeader(ctx, blockHeader) if err != nil { return err @@ -317,11 +329,18 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum ui if err != nil { return err } - message, err := m.txStreamer.GetMessage(blockNum) + genesisBlockNum, err := m.txStreamer.GetGenesisBlockNumber() + if err != nil { + return err + } + message, err := m.txStreamer.GetMessage(uint64(blockNum) - genesisBlockNum) if err != nil { return err } - nextHeader := m.blockchain.GetHeaderByNumber(blockNum + 1) + nextHeader := m.blockchain.GetHeaderByNumber(uint64(blockNum + 1)) + if nextHeader == nil { + return fmt.Errorf("next block header %v after challenge point unknown", blockNum+1) + } preimages, hasDelayedMsg, delayedMsgNr, err := BlockDataForValidation(m.blockchain, nextHeader, blockHeader, message) if err != nil { return err @@ -359,7 +378,11 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { return nil } - inExec, addr, blockNum, err := m.blockChallengeBackend.IsInExecutionChallenge(ctx) + latestConfirmedBlock, err := m.latestConfirmedBlock(ctx) + if err != nil { + return err + } + inExec, addr, blockNum, err := m.blockChallengeBackend.IsInExecutionChallenge(ctx, latestConfirmedBlock) if err != nil || !inExec { return err } diff --git a/validator/staker.go b/validator/staker.go index 5afbbf2206..35bad6f9df 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -45,6 +45,7 @@ type ValidatorConfig struct { DontChallenge bool WithdrawDestination string TargetNumMachines int + ConfirmationBlocks int64 } type nodeAndHash struct { @@ -357,7 +358,7 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { if s.activeChallenge == nil || s.activeChallenge.RootChallengeAddress() != *info.CurrentChallenge { log.Warn("entered challenge", "challenge", info.CurrentChallenge) - newChallengeManager, err := NewChallengeManager(ctx, s.client, s.auth, *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, uint64(s.fromBlock), s.config.TargetNumMachines) + newChallengeManager, err := NewChallengeManager(ctx, s.builder, s.builder.builderAuth, *s.builder.wallet.Address(), *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, uint64(s.fromBlock), s.config.TargetNumMachines, s.config.ConfirmationBlocks) if err != nil { return err } From 3f40003a485f86deac1a1fed495801187666ab53 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 13:03:32 -0600 Subject: [PATCH 021/110] Fix various things about proving the genesis block --- arbnode/transaction_streamer.go | 12 ++++++++++-- go-ethereum | 2 +- validator/block_challenge_backend.go | 21 ++++++++++----------- validator/challenge_manager.go | 2 +- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index edc4f58148..304d87de14 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -317,7 +317,11 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa if lastBlockHeader == nil { return errors.New("current block header not found") } - if lastBlockHeader.Number.Uint64() != pos { + genesisBlock, err := s.GetGenesisBlockNumber() + if err != nil { + return err + } + if lastBlockHeader.Number.Uint64()+1-genesisBlock != pos { return errors.New("block production not caught up") } statedb, err := s.bc.StateAt(lastBlockHeader.Root) @@ -475,7 +479,11 @@ func (s *TransactionStreamer) createBlocks(ctx context.Context) error { if lastBlockHeader == nil { return errors.New("current block header not found") } - pos := lastBlockHeader.Number.Uint64() + genesisBlock, err := s.GetGenesisBlockNumber() + if err != nil { + return err + } + pos := lastBlockHeader.Number.Uint64() + 1 - genesisBlock statedb, err := s.bc.StateAt(lastBlockHeader.Root) if err != nil { return err diff --git a/go-ethereum b/go-ethereum index b5b11f3a51..bdf5436449 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b5b11f3a516d1a22fe4dfd79580a3594f95f2262 +Subproject commit bdf5436449917d6147f9531d3fbd4737ed7b52b1 diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 2fb08f7ba0..439fd15b60 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -107,10 +107,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, errors.Wrap(err, "failed to get challenge start batch metadata") } } - var expectedMsgCount uint64 - if startBlockNum > 0 { - expectedMsgCount = uint64(startBlockNum) - genesisBlockNum - } + expectedMsgCount := uint64(startBlockNum+1) - genesisBlockNum if startMsgCount != expectedMsgCount { return nil, errors.New("start block and start message count are not 1:1") } @@ -179,6 +176,9 @@ func (b *BlockChallengeBackend) findBatchFromMessageCount(ctx context.Context, m } func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, header *types.Header) (GoGlobalState, error) { + if header == nil { + return GoGlobalState{}, nil + } msgCount := header.Number.Uint64() batch, err := b.findBatchFromMessageCount(ctx, msgCount) if err != nil { @@ -213,13 +213,12 @@ func (b *BlockChallengeBackend) GetInfoAtStep(ctx context.Context, step uint64) return GoGlobalState{}, STATUS_TOO_FAR, nil } blockNum := b.GetBlockNrAtStep(step) - if blockNum == -1 { - // Pre-genesis state - return GoGlobalState{}, STATUS_FINISHED, nil - } - header := b.bc.GetHeaderByNumber(uint64(blockNum)) - if header == nil { - return GoGlobalState{}, 0, errors.New("failed to get block in block challenge") + var header *types.Header + if blockNum != -1 { + header = b.bc.GetHeaderByNumber(uint64(blockNum)) + if header == nil { + return GoGlobalState{}, 0, fmt.Errorf("failed to get block %v in block challenge", blockNum) + } } globalState, err := b.FindGlobalStateFromHeader(ctx, header) if err != nil { diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index e83d64a868..df85baaeed 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -333,7 +333,7 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in if err != nil { return err } - message, err := m.txStreamer.GetMessage(uint64(blockNum) - genesisBlockNum) + message, err := m.txStreamer.GetMessage(uint64(blockNum+1) - genesisBlockNum) if err != nil { return err } From 0ff6df1b23d70a0425e0d453c5f0a5a527123f91 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 17:35:52 -0600 Subject: [PATCH 022/110] Remove staker strategy being double specified in params --- system_tests/full_challenge_test.go | 8 ++++---- system_tests/staker_test.go | 15 ++++++++++++--- validator/staker.go | 14 +++++++++++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 73a35379c0..ee245b7674 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -101,10 +101,10 @@ func CreateChallenge( tx, err = blockChallengeFactory.CreateChallenge( auth, - [3]common.Address{ - resultReceiverAddr, - sequencerInbox, - delayedBridge, + challengegen.IBlockChallengeFactoryChallengeContracts{ + ResultReceiver: resultReceiverAddr, + SequencerInbox: sequencerInbox, + DelayedBridge: delayedBridge, }, wasmModuleRoot, [2]uint8{ diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 5302ac279e..4b47b96b49 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -86,6 +86,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) Require(t, err) + tx, err = rollup.SetMinimumAssertionPeriod(&deployAuth, big.NewInt(1)) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, tx) + Require(t, err) + valConfig := validator.ValidatorConfig{ UtilsAddress: valUtils.Hex(), TargetNumMachines: 4, @@ -93,12 +98,16 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) valWalletA, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeA.DeployInfo.Rollup, l1client, &l1authA, 0, func(common.Address) {}) Require(t, err) + if honestStakerInactive { + valConfig.Strategy = "Defensive" + } else { + valConfig.Strategy = "MakeNodes" + } stakerA, err := validator.NewStaker( ctx, l1client, valWalletA, 0, - validator.MakeNodesStrategy, bind.CallOpts{}, &l1authA, valConfig, @@ -112,12 +121,12 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) valWalletB, err := validator.NewValidatorWallet(nil, valWalletFactory, l2nodeB.DeployInfo.Rollup, l1client, &l1authB, 0, func(common.Address) {}) Require(t, err) + valConfig.Strategy = "MakeNodes" stakerB, err := validator.NewStaker( ctx, l1client, valWalletB, 0, - validator.StakeLatestStrategy, bind.CallOpts{}, &l1authB, valConfig, @@ -206,7 +215,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) if isHonestZombie { t.Fatal("staker A became a zombie") } - for j := 0; j < 10; j++ { + for j := 0; j < 5; j++ { TransferBalance(t, "Faucet", "Faucet", common.Big0, l1info, l1client, ctx) } } diff --git a/validator/staker.go b/validator/staker.go index 35bad6f9df..eec37e8c1c 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "math/big" + "strings" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -78,7 +79,6 @@ func NewStaker( client *ethclient.Client, wallet *ValidatorWallet, fromBlock int64, - strategy StakerStrategy, callOpts bind.CallOpts, auth *bind.TransactOpts, config ValidatorConfig, @@ -92,6 +92,18 @@ func NewStaker( return nil, fmt.Errorf("invalid validator utils address \"%v\"", config.UtilsAddress) } validatorUtilsAddress := common.HexToAddress(config.UtilsAddress) + var strategy StakerStrategy + if strings.ToLower(config.Strategy) == "watchtower" { + strategy = WatchtowerStrategy + } else if strings.ToLower(config.Strategy) == "defensive" { + strategy = DefensiveStrategy + } else if strings.ToLower(config.Strategy) == "stakelatest" { + strategy = StakeLatestStrategy + } else if strings.ToLower(config.Strategy) == "makenodes" { + strategy = MakeNodesStrategy + } else { + return nil, fmt.Errorf("unknown staker strategy \"%v\"", config.Strategy) + } val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) if err != nil { return nil, err From e608471e746ed1f03903561d84eb4fea78525f9d Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 17:46:50 -0600 Subject: [PATCH 023/110] Fix tx streamer and tx streamer test --- arbnode/inbox_test.go | 16 +++++++++++++++- arbnode/transaction_streamer.go | 11 +++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index a2d0357736..162d5c4540 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -53,6 +53,20 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* Fail(t, err) } + // Add the init message + err = inbox.AddMessages(0, false, []arbstate.MessageWithMetadata{{ + Message: &arbos.L1IncomingMessage{ + Header: &arbos.L1IncomingMessageHeader{ + Kind: arbos.L1MessageType_SetChainParams, + }, + L2msg: []byte{}, + }, + DelayedMessagesRead: 0, + }}) + if err != nil { + Fail(t, err) + } + return inbox, bc } @@ -84,7 +98,7 @@ func TestTransactionStreamer(t *testing.T) { rewrittenOwnerAddress: new(big.Int).Mul(maxExpectedGasCost, big.NewInt(1_000_000)), }, accounts: []common.Address{rewrittenOwnerAddress}, - numMessages: 0, + numMessages: 1, blockNumber: 0, }) for i := 1; i < 100; i++ { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 304d87de14..8be1a6ff8f 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -121,16 +121,23 @@ func deleteStartingAt(db ethdb.Database, batch ethdb.Batch, prefix []byte, minKe } func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count uint64) error { + if count == 0 { + return errors.New("cannot reorg out init message") + } atomic.AddUint32(&s.reorgPending, 1) s.reorgMutex.Lock() defer s.reorgMutex.Unlock() atomic.AddUint32(&s.reorgPending, ^uint32(0)) // decrement - targetBlock := s.bc.GetBlockByNumber(count) + genesisBlock, err := s.GetGenesisBlockNumber() + if err != nil { + return err + } + targetBlock := s.bc.GetBlockByNumber(count + genesisBlock - 1) if targetBlock == nil { return errors.New("reorg target block not found") } - err := s.bc.ReorgToOldBlock(targetBlock) + err = s.bc.ReorgToOldBlock(targetBlock) if err != nil { return err } From 1156ad70d17fd84f4683b83f25e3a1e77d639ce3 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 18:31:53 -0600 Subject: [PATCH 024/110] Set nonce of genesis block to 1, as it reads the init message --- arbnode/node.go | 2 +- validator/block_validator.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index 1e8ceba04a..a166b72c55 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -471,7 +471,7 @@ func WriteOrTestGenblock(chainDb ethdb.Database, initData *statetransfer.ArbosIn } head := &types.Header{ Number: new(big.Int).SetUint64(blockNumber), - Nonce: types.EncodeNonce(0), + Nonce: types.EncodeNonce(1), Time: 0, ParentHash: prevHash, Extra: []byte("ArbitrumMainnet"), diff --git a/validator/block_validator.go b/validator/block_validator.go index dfe5739f38..75480f520f 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -87,7 +87,7 @@ type GlobalStatePosition struct { PosInBatch uint64 } -func GlobalStatePoisionsFor(reader InboxTrackerInterface, pos uint64, batch uint64) (GlobalStatePosition, GlobalStatePosition, error) { +func GlobalStatePositionsFor(reader InboxTrackerInterface, pos uint64, batch uint64) (GlobalStatePosition, GlobalStatePosition, error) { msgCountInBatch, err := reader.GetBatchMessageCount(batch) if err != nil { return GlobalStatePosition{}, GlobalStatePosition{}, err @@ -497,7 +497,7 @@ func (v *BlockValidator) sendValidations(ctx context.Context) { log.Error("bad entry trying to validate batch") return } - startPos, endPos, err := GlobalStatePoisionsFor(v.inboxTracker, v.posNextSend, v.globalPosNextSend.BatchNumber) + startPos, endPos, err := GlobalStatePositionsFor(v.inboxTracker, v.posNextSend, v.globalPosNextSend.BatchNumber) if err != nil { log.Error("failed calculating position for validation", "err", err, "pos", v.posNextSend, "batch", v.globalPosNextSend.BatchNumber) } From 938bd18a9f5c67fbb053eb8a7a1efa0ea67045e9 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Thu, 17 Feb 2022 20:24:04 -0500 Subject: [PATCH 025/110] Improve staker query efficiency --- system_tests/staker_test.go | 2 - validator/l1_validator.go | 10 ++--- validator/rollup_watcher.go | 74 +++++++++++++++++++++++++++-------- validator/staker.go | 16 ++++---- validator/validator_wallet.go | 1 + 5 files changed, 72 insertions(+), 31 deletions(-) diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 4b47b96b49..7a64cc9c8d 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -107,7 +107,6 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) ctx, l1client, valWalletA, - 0, bind.CallOpts{}, &l1authA, valConfig, @@ -126,7 +125,6 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) ctx, l1client, valWalletB, - 0, bind.CallOpts{}, &l1authB, valConfig, diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 2047fed6c5..078457ce20 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -47,7 +47,6 @@ func NewValidator( ctx context.Context, client arbutil.L1Interface, wallet *ValidatorWallet, - fromBlock int64, validatorUtilsAddress common.Address, callOpts bind.CallOpts, l2Blockchain *core.BlockChain, @@ -60,8 +59,7 @@ func NewValidator( if err != nil { return nil, err } - rollup, err := NewRollupWatcher(wallet.RollupAddress(), fromBlock, builder, callOpts) - _ = rollup + rollup, err := NewRollupWatcher(ctx, wallet.RollupAddress(), builder, callOpts) if err != nil { return nil, err } @@ -150,7 +148,7 @@ func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*types.Trans return v.wallet.TimeoutChallenges(ctx, challengesToEliminate) } -func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo, fromBlock int64) error { +func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo) error { callOpts := v.getCallOpts(ctx) confirmType, err := v.validatorUtils.CheckDecidableNextNode(callOpts, v.rollupAddress) if err != nil { @@ -241,7 +239,7 @@ func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (uint64, bool, return v.genesisBlockNumber + batchHeight + gs.PosInBatch, false, nil } -func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy, fromBlock int64) (nodeAction, bool, error) { +func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy) (nodeAction, bool, error) { startState, prevInboxMaxCount, startStateProposed, err := lookupNodeStartState(ctx, v.rollup, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) if err != nil { return nil, false, err @@ -313,7 +311,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } // Not necessarily successors - successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNodeHash, startStateProposed) + successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNode) if err != nil { return nil, false, err } diff --git a/validator/rollup_watcher.go b/validator/rollup_watcher.go index 9f3de908df..d24ba91874 100644 --- a/validator/rollup_watcher.go +++ b/validator/rollup_watcher.go @@ -41,21 +41,28 @@ type StakerInfo struct { type RollupWatcher struct { address common.Address - fromBlock int64 + fromBlock uint64 client arbutil.L1Interface baseCallOpts bind.CallOpts *rollupgen.RollupUserLogic } -func NewRollupWatcher(address common.Address, fromBlock int64, client arbutil.L1Interface, callOpts bind.CallOpts) (*RollupWatcher, error) { +func NewRollupWatcher(ctx context.Context, address common.Address, client arbutil.L1Interface, callOpts bind.CallOpts) (*RollupWatcher, error) { con, err := rollupgen.NewRollupUserLogic(address, client) if err != nil { return nil, errors.WithStack(err) } + opts := callOpts + opts.Context = ctx + firstNode, err := con.GetNode(&opts, 0) + if err != nil { + return nil, err + } + return &RollupWatcher{ address: address, - fromBlock: fromBlock, + fromBlock: firstNode.CreatedAtBlock, client: client, baseCallOpts: callOpts, RollupUserLogic: con, @@ -71,11 +78,10 @@ func (r *RollupWatcher) getCallOpts(ctx context.Context) *bind.CallOpts { func (r *RollupWatcher) LookupCreation(ctx context.Context) (*rollupgen.RollupUserLogicRollupInitialized, error) { var toBlock *big.Int if r.fromBlock > 0 { - toBlock = big.NewInt(r.fromBlock) + toBlock = new(big.Int).SetUint64(r.fromBlock) } var query = ethereum.FilterQuery{ - BlockHash: nil, - FromBlock: big.NewInt(r.fromBlock), + FromBlock: new(big.Int).SetUint64(r.fromBlock), ToBlock: toBlock, Addresses: []common.Address{r.address}, Topics: [][]common.Hash{{rollupInitializedID}}, @@ -95,12 +101,15 @@ func (r *RollupWatcher) LookupCreation(ctx context.Context) (*rollupgen.RollupUs } func (r *RollupWatcher) LookupNode(ctx context.Context, number uint64) (*NodeInfo, error) { + node, err := r.GetNode(r.getCallOpts(ctx), number) + if err != nil { + return nil, errors.WithStack(err) + } var numberAsHash common.Hash binary.BigEndian.PutUint64(numberAsHash[(32-8):], number) var query = ethereum.FilterQuery{ - BlockHash: nil, - FromBlock: big.NewInt(r.fromBlock), - ToBlock: nil, + FromBlock: new(big.Int).SetUint64(node.CreatedAtBlock), + ToBlock: new(big.Int).SetUint64(node.CreatedAtBlock), Addresses: []common.Address{r.address}, Topics: [][]common.Hash{{nodeCreatedID}, {numberAsHash}}, } @@ -130,13 +139,23 @@ func (r *RollupWatcher) LookupNode(ctx context.Context, number uint64) (*NodeInf }, nil } -func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, parentHash [32]byte, fromBlock uint64) ([]*NodeInfo, error) { +func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64) ([]*NodeInfo, error) { + node, err := r.RollupUserLogic.GetNode(r.getCallOpts(ctx), nodeNum) + if err != nil { + return nil, errors.WithStack(err) + } + if node.LatestChildNumber == 0 { + return nil, nil + } + latestChild, err := r.RollupUserLogic.GetNode(r.getCallOpts(ctx), node.LatestChildNumber) + if err != nil { + return nil, errors.WithStack(err) + } var query = ethereum.FilterQuery{ - BlockHash: nil, - FromBlock: new(big.Int).SetUint64(fromBlock), - ToBlock: nil, + FromBlock: new(big.Int).SetUint64(node.CreatedAtBlock), + ToBlock: new(big.Int).SetUint64(latestChild.CreatedAtBlock), Addresses: []common.Address{r.address}, - Topics: [][]common.Hash{{nodeCreatedID}, nil, {parentHash}}, + Topics: [][]common.Hash{{nodeCreatedID}, nil, {node.NodeHash}}, } logs, err := r.client.FilterLogs(ctx, query) if err != nil { @@ -162,16 +181,39 @@ func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, parentHash [32]b WasmModuleRoot: parsedLog.WasmModuleRoot, }) } + // TODO: If we want to verify consistency here, we can that that the node hash of the last node + // found matches latestChild since it encompasses all preceding nodes return infos, nil } +func (r *RollupWatcher) LatestConfirmedCreationBlock(ctx context.Context) (uint64, error) { + latestConfirmed, err := r.LatestConfirmed(r.getCallOpts(ctx)) + if err != nil { + return 0, errors.WithStack(err) + } + latestConfirmedNode, err := r.GetNode(r.getCallOpts(ctx), latestConfirmed) + if err != nil { + return 0, errors.WithStack(err) + } + return latestConfirmedNode.CreatedAtBlock, nil +} + func (r *RollupWatcher) LookupChallengedNode(ctx context.Context, address common.Address) (uint64, error) { + // TODO: This function is currently unused + + // Assuming this function is only used to find information about an active challenge, it + // must be a challenge over an unconfirmed node and thus must have been created after the + // latest confirmed node was created + latestConfirmedCreated, err := r.LatestConfirmedCreationBlock(ctx) + if err != nil { + return 0, err + } + addressQuery := common.Hash{} copy(addressQuery[12:], address.Bytes()) query := ethereum.FilterQuery{ - BlockHash: nil, - FromBlock: big.NewInt(r.fromBlock), + FromBlock: new(big.Int).SetUint64(latestConfirmedCreated), ToBlock: nil, Addresses: []common.Address{r.address}, Topics: [][]common.Hash{{challengeCreatedID}, {addressQuery}}, diff --git a/validator/staker.go b/validator/staker.go index eec37e8c1c..ef221e06d0 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -58,7 +58,6 @@ type Staker struct { *Validator activeChallenge *ChallengeManager strategy StakerStrategy - fromBlock int64 baseCallOpts bind.CallOpts auth *bind.TransactOpts config ValidatorConfig @@ -78,7 +77,6 @@ func NewStaker( ctx context.Context, client *ethclient.Client, wallet *ValidatorWallet, - fromBlock int64, callOpts bind.CallOpts, auth *bind.TransactOpts, config ValidatorConfig, @@ -104,7 +102,7 @@ func NewStaker( } else { return nil, fmt.Errorf("unknown staker strategy \"%v\"", config.Strategy) } - val, err := NewValidator(ctx, client, wallet, fromBlock, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) + val, err := NewValidator(ctx, client, wallet, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) if err != nil { return nil, err } @@ -115,7 +113,6 @@ func NewStaker( return &Staker{ Validator: val, strategy: strategy, - fromBlock: fromBlock, baseCallOpts: callOpts, auth: auth, config: config, @@ -300,7 +297,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { if err != nil || arbTx != nil { return arbTx, err } - if err := s.resolveNextNode(ctx, rawInfo, s.fromBlock); err != nil { + if err := s.resolveNextNode(ctx, rawInfo); err != nil { return nil, err } } @@ -370,7 +367,12 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { if s.activeChallenge == nil || s.activeChallenge.RootChallengeAddress() != *info.CurrentChallenge { log.Warn("entered challenge", "challenge", info.CurrentChallenge) - newChallengeManager, err := NewChallengeManager(ctx, s.builder, s.builder.builderAuth, *s.builder.wallet.Address(), *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, uint64(s.fromBlock), s.config.TargetNumMachines, s.config.ConfirmationBlocks) + latestConfirmedCreated, err := s.rollup.LatestConfirmedCreationBlock(ctx) + if err != nil { + return err + } + + newChallengeManager, err := NewChallengeManager(ctx, s.builder, s.builder.builderAuth, *s.builder.wallet.Address(), *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, latestConfirmedCreated, s.config.TargetNumMachines, s.config.ConfirmationBlocks) if err != nil { return err } @@ -406,7 +408,7 @@ func (s *Staker) newStake(ctx context.Context) error { func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { active := effectiveStrategy >= StakeLatestStrategy - action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, s.fromBlock) + action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy) if err != nil { return err } diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go index f70e2b2e79..1a8c9aabf5 100644 --- a/validator/validator_wallet.go +++ b/validator/validator_wallet.go @@ -186,6 +186,7 @@ func CreateValidatorWallet( transactAuth *bind.TransactOpts, client arbutil.L1Interface, ) (common.Address, error) { + // TODO: If we just save a mapping in the wallet creator we won't need log search walletCreator, err := rollupgen.NewValidatorWalletCreator(validatorWalletFactoryAddr, client) if err != nil { return common.Address{}, errors.WithStack(err) From a624784815d2bf10edac2b5dc37618be36d59a43 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 19:49:11 -0600 Subject: [PATCH 026/110] Fix block validator assumption that block num = msg seq num --- arbnode/node.go | 5 ++++- validator/block_validator.go | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index a166b72c55..fe8f2636d5 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -349,7 +349,10 @@ func CreateNode(stack *node.Node, chainDb ethdb.Database, config *NodeConfig, l2 var blockValidator *validator.BlockValidator if config.BlockValidator { - blockValidator = validator.NewBlockValidator(inboxTracker, txStreamer, l2BlockChain, &config.BlockValidatorConfig) + blockValidator, err = validator.NewBlockValidator(inboxTracker, txStreamer, l2BlockChain, &config.BlockValidatorConfig) + if err != nil { + return nil, err + } } if !config.BatchPoster { diff --git a/validator/block_validator.go b/validator/block_validator.go index 75480f520f..2642dc4363 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -26,8 +26,9 @@ import ( ) type BlockValidator struct { - inboxTracker InboxTrackerInterface - blockchain *core.BlockChain + inboxTracker InboxTrackerInterface + blockchain *core.BlockChain + genesisBlockNum uint64 validationEntries sync.Map sequencerBatches sync.Map @@ -148,25 +149,34 @@ func newValidationEntry(prevHeader *types.Header, header *types.Header, hasDelay }, nil } -func NewBlockValidator(inbox InboxTrackerInterface, streamer TransactionStreamerInterface, blockchain *core.BlockChain, config *BlockValidatorConfig) *BlockValidator { +func NewBlockValidator(inbox InboxTrackerInterface, streamer TransactionStreamerInterface, blockchain *core.BlockChain, config *BlockValidatorConfig) (*BlockValidator, error) { CreateHostIoMachine() concurrent := config.ConcurrentRunsLimit if concurrent == 0 { concurrent = runtime.NumCPU() } + genesisBlockNum, err := streamer.GetGenesisBlockNumber() + if err != nil { + return nil, err + } validator := &BlockValidator{ inboxTracker: inbox, blockchain: blockchain, - posNextSend: 0, sendValidationsChan: make(chan interface{}), checkProgressChan: make(chan interface{}), progressChan: make(chan uint64), concurrentRunsLimit: int32(concurrent), config: config, + genesisBlockNum: genesisBlockNum, + // TODO: this skips validating the genesis block + posNextSend: 1, + globalPosNextSend: GlobalStatePosition{ + BatchNumber: 1, + }, } streamer.SetBlockValidator(validator) inbox.SetBlockValidator(validator) - return validator + return validator, nil } func RecordBlockCreation(blockchain *core.BlockChain, prevHeader *types.Header, msg arbstate.MessageWithMetadata) (common.Hash, map[common.Hash][]byte, error) { @@ -487,8 +497,9 @@ func (v *BlockValidator) sendValidations(ctx context.Context) { if !haveBatch { return } - // valdationEntries is By blockNumber, which is one more than pos - entry, found := v.validationEntries.Load(v.posNextSend + 1) + // valdationEntries is By blockNumber + blockNum := v.posNextSend + v.genesisBlockNum + entry, found := v.validationEntries.Load(blockNum) if !found { return } @@ -500,6 +511,7 @@ func (v *BlockValidator) sendValidations(ctx context.Context) { startPos, endPos, err := GlobalStatePositionsFor(v.inboxTracker, v.posNextSend, v.globalPosNextSend.BatchNumber) if err != nil { log.Error("failed calculating position for validation", "err", err, "pos", v.posNextSend, "batch", v.globalPosNextSend.BatchNumber) + return } if startPos != v.globalPosNextSend { log.Error("inconsistent pos mapping", "uint_pos", v.posNextSend, "expected", v.globalPosNextSend, "found", startPos) From 590a00fb98eca6998c9deb94ab575eaaec8f1f65 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 20:09:11 -0600 Subject: [PATCH 027/110] Fix test assumptions about init messages --- system_tests/common_test.go | 16 ++++++++++++++++ system_tests/seqinbox_test.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/system_tests/common_test.go b/system_tests/common_test.go index f7f647ab0b..0309ef4a86 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -10,6 +10,8 @@ import ( "testing" "time" + "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbstate" "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/statetransfer" @@ -17,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/arbitrum" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" @@ -195,6 +198,19 @@ func CreateTestL2WithConfig(t *testing.T, ctx context.Context, l2Info *Blockchai l2info, stack, chainDb, blockchain := createL2BlockChain(t, l2Info) node, err := arbnode.CreateNode(stack, chainDb, nodeConfig, blockchain, nil, nil, nil) Require(t, err) + + // Give the node an init message + err = node.TxStreamer.AddMessages(0, false, []arbstate.MessageWithMetadata{{ + Message: &arbos.L1IncomingMessage{ + Header: &arbos.L1IncomingMessageHeader{ + Kind: arbos.L1MessageType_SetChainParams, + }, + L2msg: math.U256Bytes(l2info.Signer.ChainID()), + }, + DelayedMessagesRead: 0, + }}) + Require(t, err) + Require(t, node.Start(ctx)) client := ClientForArbBackend(t, node.Backend) diff --git a/system_tests/seqinbox_test.go b/system_tests/seqinbox_test.go index c39348030b..417ea58595 100644 --- a/system_tests/seqinbox_test.go +++ b/system_tests/seqinbox_test.go @@ -50,7 +50,7 @@ func TestSequencerInboxReader(t *testing.T) { seqOpts := l1Info.GetDefaultTransactOpts("Sequencer") ownerAddress := l2Info.GetAddress("Owner") - var startL2BlockNumber uint64 = 1 + var startL2BlockNumber uint64 = 0 startState, _, err := l2Backend.APIBackend().StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber) Require(t, err) From 5e82f911e68b8e8597e3863dbded7e73766ddace Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Thu, 17 Feb 2022 20:53:46 -0600 Subject: [PATCH 028/110] Improve block production not caught up error --- arbnode/transaction_streamer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 8be1a6ff8f..361f03fbc7 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -329,7 +329,7 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa return err } if lastBlockHeader.Number.Uint64()+1-genesisBlock != pos { - return errors.New("block production not caught up") + return fmt.Errorf("block production not caught up: last block number %v but expected %v", lastBlockHeader.Number, int64(pos)-1+int64(genesisBlock)) } statedb, err := s.bc.StateAt(lastBlockHeader.Root) if err != nil { From a9140af713a1d91eda7378d0ca687a6a54b645ec Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Thu, 17 Feb 2022 22:56:43 -0600 Subject: [PATCH 029/110] support tips in excess of basefee --- arbos/block_processor.go | 2 +- arbos/l1pricing/l1pricing.go | 21 +++++++++++---------- arbos/tx_processor.go | 36 +++++++++++++++++++++++++++++++----- go-ethereum | 2 +- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/arbos/block_processor.go b/arbos/block_processor.go index 2e7c0651db..6f964c4db5 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -215,7 +215,7 @@ func ProduceBlockAdvanced( if gasPrice.Sign() > 0 { dataGas = math.MaxUint64 pricing := state.L1PricingState() - posterCost, _ := pricing.PosterDataCost(sender, aggregator, tx.Data()) + posterCost, _, _ := pricing.PosterDataCost(sender, aggregator, tx.Data()) posterCostInL2Gas := new(big.Int).Div(posterCost, gasPrice) if posterCostInL2Gas.IsUint64() { dataGas = posterCostInL2Gas.Uint64() diff --git a/arbos/l1pricing/l1pricing.go b/arbos/l1pricing/l1pricing.go index 3ee7eb82d4..511c7edf8d 100644 --- a/arbos/l1pricing/l1pricing.go +++ b/arbos/l1pricing/l1pricing.go @@ -5,9 +5,10 @@ package l1pricing import ( - "github.com/offchainlabs/arbstate/arbos/addressSet" "math/big" + "github.com/offchainlabs/arbstate/arbos/addressSet" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/arbstate/arbos/storage" @@ -217,26 +218,26 @@ func (ps *L1PricingState) PosterDataCost( sender common.Address, aggregator *common.Address, data []byte, -) (*big.Int, error) { +) (*big.Int, bool, error) { if aggregator == nil { - return big.NewInt(0), nil + return big.NewInt(0), false, nil } reimbursableAggregator, err := ps.ReimbursableAggregatorForSender(sender) if err != nil { - return nil, err + return nil, false, err } if reimbursableAggregator == nil { - return big.NewInt(0), nil + return big.NewInt(0), false, nil } if *reimbursableAggregator != *aggregator { - return big.NewInt(0), nil + return big.NewInt(0), false, nil } bytesToCharge := uint64(len(data) + TxFixedCost) ratio, err := ps.AggregatorCompressionRatio(*reimbursableAggregator) if err != nil { - return nil, err + return nil, false, err } dataGas := 16 * bytesToCharge * ratio / DataWasNotCompressed @@ -246,14 +247,14 @@ func (ps *L1PricingState) PosterDataCost( price, err := ps.L1GasPriceEstimateWei() if err != nil { - return nil, err + return nil, false, err } baseCharge, err := ps.FixedChargeForAggregatorWei(*reimbursableAggregator) if err != nil { - return nil, err + return nil, false, err } chargeForBytes := new(big.Int).Mul(big.NewInt(int64(dataGas)), price) - return new(big.Int).Add(baseCharge, chargeForBytes), nil + return new(big.Int).Add(baseCharge, chargeForBytes), true, nil } diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 9dc957c7b2..241d2fe7de 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -37,6 +37,8 @@ type TxProcessor struct { state *arbosState.ArbosState PosterFee *big.Int // set once in GasChargingHook to track L1 calldata costs posterGas uint64 + tipWeiPerGas *big.Int + tipRecipient common.Address computeHoldGas uint64 // amount of gas temporarily held to prevent compute from exceeding the gas limit Callers []common.Address TopTxType *byte // set once in StartTxHook @@ -50,11 +52,17 @@ func NewTxProcessor(evm *vm.EVM, msg core.Message) *TxProcessor { panic(err) } arbosState.SetLastTimestampSeen(evm.Context.Time.Uint64()) + networkFeeAccount, err := arbosState.NetworkFeeAccount() + if err != nil { + panic(err) + } return &TxProcessor{ msg: msg, state: arbosState, PosterFee: new(big.Int), posterGas: 0, + tipWeiPerGas: new(big.Int), + tipRecipient: networkFeeAccount, Callers: []common.Address{}, TopTxType: nil, evm: evm, @@ -215,16 +223,18 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r return false, 0, nil, nil } -func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { +func (p *TxProcessor) GasChargingHook(gasRemaining *uint64, txGasPrice *big.Int) error { // Because a user pays a 1-dimensional gas price, we must re-express poster L1 calldata costs // as if the user was buying an equivalent amount of L2 compute gas. This hook determines what // that cost looks like, ensuring the user can pay and saving the result for later reference. var gasNeededToStartEVM uint64 + from := p.msg.From() gasPrice := p.evm.Context.BaseFee l1Pricing := p.state.L1PricingState() - posterCost, err := l1Pricing.PosterDataCost(p.msg.From(), p.getReimbursableAggregator(), p.msg.Data()) + aggregator := p.getReimbursableAggregator() + posterCost, reimbursable, err := l1Pricing.PosterDataCost(from, aggregator, p.msg.Data()) p.state.Restrict(err) if p.msg.RunMode() == types.MessageGasEstimationMode { @@ -251,6 +261,20 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { p.posterGas = posterCostInL2Gas.Uint64() p.PosterFee = new(big.Int).Mul(posterCostInL2Gas, gasPrice) // round down gasNeededToStartEVM = p.posterGas + + // Most users shouldn't set a tip, but if one is specified ensure the user has enough funds to + // tip the appropriate party (the poster, if reimbursable, otherwise the network fee account) + if txGasPrice != nil && util.BigGreaterThan(txGasPrice, gasPrice) { + p.tipWeiPerGas = util.BigSub(txGasPrice, gasPrice) + } + if reimbursable && aggregator != nil { + p.tipRecipient = *aggregator + } + tip := util.BigMulByUint(p.tipWeiPerGas, p.msg.Gas()) + if util.BigLessThan(p.evm.StateDB.GetBalance(from), tip) { + return core.ErrInsufficientFunds + } + p.evm.StateDB.SubBalance(from, tip) } if *gasRemaining < gasNeededToStartEVM { @@ -327,10 +351,10 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { if gasLeft > p.msg.Gas() { panic("Tx somehow refunds gas after computation") } - gasUsed := new(big.Int).SetUint64(p.msg.Gas() - gasLeft) + gasUsed := util.UintToBig(p.msg.Gas() - gasLeft) - totalCost := new(big.Int).Mul(gasPrice, gasUsed) // total cost = price of gas * gas burnt - computeCost := new(big.Int).Sub(totalCost, p.PosterFee) // total cost = network's compute + poster's L1 costs + totalCost := util.BigMul(gasPrice, gasUsed) // total cost = price of gas * gas burnt + computeCost := util.BigSub(totalCost, p.PosterFee) // total cost = network's compute + poster's L1 costs if computeCost.Sign() < 0 { // Uh oh, there's a bug in our charging code. // Give all funds to the network account and continue. @@ -342,6 +366,8 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { p.evm.StateDB.AddBalance(networkFeeAccount, computeCost) p.evm.StateDB.AddBalance(p.evm.Context.Coinbase, p.PosterFee) + p.evm.StateDB.AddBalance(p.tipRecipient, util.BigMul(p.tipWeiPerGas, gasUsed)) // tip on gas used + p.evm.StateDB.AddBalance(p.msg.From(), util.BigMulByUint(p.tipWeiPerGas, gasLeft)) // refund unused tip if p.msg.GasPrice().Sign() > 0 { // in tests, gas price coud be 0 // ArbOS's gas pool is meant to enforce the computational speed-limit. diff --git a/go-ethereum b/go-ethereum index b5b11f3a51..86de72bf99 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b5b11f3a516d1a22fe4dfd79580a3594f95f2262 +Subproject commit 86de72bf992632b28aa32dd286e61ce909025e20 From fcd7bfb80f968130080a8fbac1581cccd07b2ca5 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Thu, 17 Feb 2022 23:00:39 -0600 Subject: [PATCH 030/110] update geth --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 86de72bf99..b943b1b07f 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 86de72bf992632b28aa32dd286e61ce909025e20 +Subproject commit b943b1b07fabb79a11f88024b86aca8fb6a32b20 From 2e7b9d5d084cc7a8474bbe38fd1b14ef7a0b69b3 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Thu, 17 Feb 2022 23:04:33 -0600 Subject: [PATCH 031/110] add tx types, underlying, and gas todos --- docs/arbos/Gas.md | 9 ++++++ docs/arbos/Geth.md | 81 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 docs/arbos/Gas.md diff --git a/docs/arbos/Gas.md b/docs/arbos/Gas.md new file mode 100644 index 0000000000..f93d70daa3 --- /dev/null +++ b/docs/arbos/Gas.md @@ -0,0 +1,9 @@ +# Gas +TODO + +## Gas Estimating Retryables + +### NodeInterface.sol +To avoid creating new RPC methods for client-side tooling, nitro geth's [`InterceptRPCMessage`][InterceptRPCMessage_link] hook provides an opportunity to swap out the message its handling before deriving a transaction from it. ArbOS uses this hook to detect messages sent to the address `0xc8`, the location of a fictional contract ... TODO + +[InterceptRPCMessage_link]: todo diff --git a/docs/arbos/Geth.md b/docs/arbos/Geth.md index 60fc9c3f0d..590c47f716 100644 --- a/docs/arbos/Geth.md +++ b/docs/arbos/Geth.md @@ -106,6 +106,77 @@ The process is simplified using two functions: [`PrepareRecording`][PrepareRecor [PrepareRecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L133 [PreimagesFromRecording_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/arbitrum/recordingdb.go#L148 +## Transaction Types + +Nitro geth includes a few L2-specific transaction types. Click on any to jump to their section. + +| Tx Type | Represents | Final hook | Source | +|:--------------------------------------------------|:----------------------------------|:---------------------------|--------| +| [`ArbitrumUnsignedTx`][ArbTxUnsigned] | | [`EndTxHook`][HE] | Bridge | +| [`ArbitrumContractTx`][ArbTxContract] | | [`EndTxHook`][HE] | Bridge | +| [`ArbitrumSubmitRetryableTx`][ArbTxSubmit]   | Creating a retryable | [`StartTxHook`][HS]   | Bridge | +| [`ArbitrumRetryTx`][ArbTxRetry] | A retryable redeem attempt   | [`EndTxHook`][HE] | L2 | +| [`ArbitrumDepositTx`][ArbTxDeposit] | A user deposit | [`StartTxHook`][HS] | Bridge | +| [`ArbitrumInternalTx`][ArbTxInternal] | ArbOS state update | [`StartTxHook`][HS] | ArbOS | + +[ArbTxUnsigned]: #ArbitrumUnsignedTx +[ArbTxContract]: #ArbitrumContractTx +[ArbTxSubmit]: #ArbitrumSubmitRetryableTx +[ArbTxRetry]: #ArbitrumRetryTx +[ArbTxDeposit]: #ArbitrumDepositTx +[ArbTxInternal]: #ArbitrumInternalTx +[HS]: #StartTxHook +[HE]: #EndTxHook + +The following reference documents each type. + +### [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link] +TODO + +### [`ArbitrumContractTx`][ArbitrumContractTx_link] +TODO + +### [`ArbitrumSubmitRetryableTx`][ArbitrumSubmitRetryableTx_link] +TODO + +### [`ArbitrumRetryTx`][ArbitrumRetryTx_link] +These are scheduled by calls to `redeem` and via retryable auto-redemption. The [retryables documentation] details ... TODO + +### [`ArbitrumDepositTx`][ArbitrumDepositTx_link] +Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. + +### [`ArbitrumInternalTx`][ArbitrumInternalTx_link] +Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a tx of this type to update its state in-between user-generated transactions. Such a tx has a [`Type`][InternalType_link] field signifying the state it will update, though currently this is just future-proofing as there's only one value it may have. Below are the internal tx types. + +#### [`ArbInternalTxUpdateL1BlockNumber`][ArbInternalTxUpdateL1BlockNumber_link] +Updates the L1 block number. This tx [is generated][block_generated_link] whenever a message originates from an L1 block newer than any ArbOS has seen thus far. They are [guaranteed to be the first][block_first_link] in their L2 block. + +[ArbitrumUnsignedTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L15 +[ArbitrumContractTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L76 +[ArbitrumSubmitRetryableTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L194 +[ArbitrumRetryTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L133 +[ArbitrumDepositTx_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L265 +[ArbitrumInternalTx_link]: https://github.com/OffchainLabs/nitro/blob/master/arbos/internal_tx.go + +[InternalType_link]: https://github.com/OffchainLabs/go-ethereum/blob/e7e8104942fd2ba676d4b8616c9fa83d88b61c9c/core/types/arb_types.go#L313 +[ArbInternalTxUpdateL1BlockNumber_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/internal_tx.go#L24 +[block_generated_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L150 +[block_first_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L154 + +## Underlying Transactions and Transaction Run Modes +Though not all geth messages are derived from a transaction, those that are carry their ... TODO + +| Run Mode | Scope | Carries an Underlying Tx? | +|:-----------------------------------------|:------------------------|:-----------------------------------------------------------------------------| +| [`MessageCommitMode`][MC0] | state transition   | always | +| [`MessageGasEstimationMode`][MC1]   | gas estimation | when created via [`NodeInterface.sol`](#NodeInterface.sol) or when scheduled | +| [`MessageEthcallMode`][MC2] | eth_calls | never | + +[MC0]: todo +[MC1]: todo +[MC2]: todo + +TODO ## Arbitrum Chain Parameters Nitro's geth may be configured with the following [l2-specific chain parameters][chain_params_link]. These allow the rollup creator to customize their rollup at genesis. @@ -133,12 +204,12 @@ The total amount of L2 ether in the system should not change except in controlle ### MixDigest and ExtraData To aid with [outbox proof construction][proof_link], the root hash and leaf count of ArbOS's [send merkle accumulator][merkle_link] are stored in the `MixDigest` and `ExtraData` fields of each L2 block. The yellow paper specifies that the `ExtraData` field may be no larger than 32 bytes, so we use the first 8 bytes of the `MixDigest`, which has no meaning in a system without miners, to store the send count. -## Retryable Support +### Retryable Support Retryables are mostly implemented in [ArbOS](ArbOS.md#retryables). Some modifications were required in geth to support them. * Added ScheduledTxes field to ExecutionResult. This lists transactions scheduled during the execution. To enable using this field, we also pass the ExecutionResult to callers of ApplyTransaction. * Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retriable activated by the original call. This allows estimating gas to enable retriables. -## Added accessors +### Added accessors Added ['UnderlyingTransaction'][UnderlyingTransaction_link] to Message interface Added ['GetCurrentTxLogs'](../../go-ethereum/core/state/statedb_arbitrum.go) to StateDB We created the AdvancedPrecompile interface, which executes and charges gas with the same function call. This is used by Arbitrum's precompiles, and also wraps geth's standard precompiles. For more information on Arbitrum precompiles, see [ArbOS doc](ArbOS.md#precompiles). @@ -146,13 +217,13 @@ We created the AdvancedPrecompile interface, which executes and charges gas with ### WASM build support The WASM arbitrum executable does not support file oprations. We created [fileutil.go](../../go-ethereum/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. ['fake_leveldb.go'](../../go-ethereum/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. -## Types +### Types Arbitrum introduces a new ['signer'](../../go-ethereum/core/types/arbitrum_signer.go), and multiple new [`transaction types`](../../go-ethereum/core/types/transaction.go). -## ReorgToOldBlock +### ReorgToOldBlock Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the ['ReorgToOldBlock'](../../go-ethereum/core/blockchain_arbitrum.go) function to support reorging to a block that's an ancestor of current head. -## Genesis block creation +### Genesis block creation Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out ['WriteHeadBlock'][WriteHeadBlock_link] from gensis.Commit and use it to commit non-zero genesis blocks. [pad_estimates_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/accounts/abi/bind/base.go#L352 From 1f2b1540d752a2faaf4434ad3a496ee8f4cf5205 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Thu, 17 Feb 2022 23:22:54 -0600 Subject: [PATCH 032/110] address Daniel's comments --- docs/arbos/ArbOS.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/arbos/ArbOS.md b/docs/arbos/ArbOS.md index 68dd3f6583..dacbc222f2 100644 --- a/docs/arbos/ArbOS.md +++ b/docs/arbos/ArbOS.md @@ -34,9 +34,9 @@ An [`L1IncomingMessage`][L1IncomingMessage_link] represents an incoming sequence ## Retryables -A Retryable is a transaction whose *submission* is separate from its *execution*. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1. If the L1 transition to request submission succeeds (i.e. does not revert) then the submission of the Retryable to the L2 state is guaranteed to succeed. +A Retryable is a transaction whose *submission* is separable from its *execution*. A retryable can be submitted for a fixed cost (dependent only on its calldata size) paid at L1. If the L1 transition to request submission succeeds (i.e. does not revert) then the submission of the Retryable to the L2 state is guaranteed to succeed. -After a Retryable is submitted, anyone can try to *redeem* it, by calling the [`redeem`](Precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](Precompiles.md#ArbRetryableTx) precompile. The party requesting the redeem provides the gas that will be used to execute the Retryable. If execution of the Retryable succeeds, the Retryable is deleted. If execution fails, the Retryable continues to exist and further attempts can be made to redeem it. If a fixed period (currently one week) elapses without a successful redeem, the Retryable expires and will be [automatically discarded][discard_link], unless some party has paid a fee to [*renew*][renew_link] the Retryable for another full period. A Retryable can live indefinitely as long as it is renewed each time before it expires. +In the common case a Retryable's submission is followed by an attempt to execute the transaction. If the attempt fails or isn't scheduled after the Retryable is submitted, anyone can try to *redeem* it, by calling the [`redeem`](Precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](Precompiles.md#ArbRetryableTx) precompile. The party requesting the redeem provides the gas that will be used to execute the Retryable. If execution of the Retryable succeeds, the Retryable is deleted. If execution fails, the Retryable continues to exist and further attempts can be made to redeem it. If a fixed period (currently one week) elapses without a successful redeem, the Retryable expires and will be [automatically discarded][discard_link], unless some party has paid a fee to [*renew*][renew_link] the Retryable for another full period. A Retryable can live indefinitely as long as it is renewed each time before it expires. [discard_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L262 [renew_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/retryables/retryable.go#L207 @@ -46,13 +46,13 @@ After a Retryable is submitted, anyone can try to *redeem* it, by calling the [` A transaction to submit a Retryable does the following: -* create a new Retryable with the caller, destination, callvalue, and calldata of the submit transaction +* create a new Retryable with the caller, destination, callvalue, calldata, and beneficiary of the submit transaction * deduct funds to cover the callvalue from the caller (as usual) and place them into escrow for later use in redeeming the Retryable * assign a unique TicketID to the Retryable * cause the ArbRetryableTx precompiled contract to emit a TicketCreated event containing the TicketID * if the submit transaction contains gas, schedule a redeem of the new Retryable, using the supplied gas, as if the [`redeem`](Precompiles.md#ArbRetryableTx) method of the [`ArbRetryableTx`](Precompiles.md#ArbRetryableTx) precompile had been called. -In many use cases, the submitter will provide gas and will intend for the immediate redeem to succeed, with later retries available only as a backup mechanism should the immediate redeem fail. (It might fail, for example, because the L2 gas price has increased unexpectedly.) In this way, an L1 contract can submit a transaction to L2 in such a way that the transaction will normally run immediately at L2 but allowing any party to retry the transaction should it fail. +In most use cases, the submitter will provide gas and will intend for the immediate redeem to succeed, with later retries available only as a backup mechanism should the immediate redeem fail. (It might fail, for example, because the L2 gas price has increased unexpectedly.) In this way, an L1 contract can submit a transaction to L2 in such a way that the transaction will normally run immediately at L2 but allowing any party to retry the transaction should it fail. When a Retryable is redeemed, it will execute with the sender, destination, callvalue, and calldata of the original submission. The callvalue will have been escrowed during the initial submission of the Retryable, for this purpose. If a Retryable with callvalue is eventually discarded, having never successfully run, the escrowed callvalue will be paid out to a "beneficiary" account that is specified in the initial submission. @@ -62,11 +62,11 @@ A Retryable's beneficiary has the unique power to [`cancel`](Precompiles.md#ArbR ### Redeeming a Retryable -If a redeem is not done at submission or the submission's initial redeem fails, anyone can attempt to redeem the retryable again by calling [`ArbRetryableTx`](Precompiles.md#ArbRetryableTx)'s [`redeem`](Precompiles.md#ArbRetryableTx) precompile method, which donates the call's gas to the next attempt. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block since the gas donated came from the pool. +If a redeem is not done at submission or the submission's initial redeem fails, anyone can attempt to redeem the retryable again by calling [`ArbRetryableTx`](Precompiles.md#ArbRetryableTx)'s [`redeem`](Precompiles.md#ArbRetryableTx) precompile method, which donates the call's gas to the next attempt. ArbOS will [enqueue the redeem][enqueue_link], which is its own special `ArbitrumRetryTx` type, to its list of redeems that ArbOS [guarantees to exhaust][exhaust_link] before moving on to the next non-redeem transaction in the block its forming. In this manner redeems are scheduled to happen as soon as possible, and will always be in the same block as the tx that scheduled it. Note that the redeem attempt's gas comes from the call to [`redeem`](Precompiles.md#ArbRetryableTx), so there's no chance the block's gas limit is reached before execution. -On success, the `To` address keeps the escrowed callvalue, and any unused gas is returned to the pools. On failure, the callvalue is returned to the escrow for the next redeemer. In either case, the network fee was paid during the scheduling tx, so no fees are charged and no refunds are made. +On success, the `To` address keeps the escrowed callvalue, and any unused gas is returned to ArbOS's gas pools. On failure, the callvalue is returned to the escrow for the next redeemer. In either case, the network fee was paid during the scheduling tx, so no fees are charged and no refunds are made. -During redemption of a retryable, attempts to cancel the same retryable, or to schedule another redeem of the same retryable, will revert. +During redemption of a retryable, attempts to cancel the same retryable, or to schedule another redeem of the same retryable, will revert. In this manner retryables are not self-modifying. [enqueue_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L245 [exhaust_link]: https://github.com/OffchainLabs/nitro/blob/fa36a0f138b8a7e684194f9840315d80c390f324/arbos/block_processor.go#L135 From 7893ee8e7c4e83787f916933c19177d0a775eefa Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 12:24:52 -0600 Subject: [PATCH 033/110] Improve batch poster --- arbnode/batch_poster.go | 25 ++++++++++++++++--------- arbutil/wait_for_l1.go | 9 ++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 2f9166a2af..05450c4ab5 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/arbstate/arbstate" @@ -287,7 +288,7 @@ func (b *BatchPoster) lastSubmissionIsSynced() bool { } // TODO make sure we detect end of block! -func (b *BatchPoster) postSequencerBatch() error { +func (b *BatchPoster) postSequencerBatch() (*types.Transaction, error) { for !b.lastSubmissionIsSynced() { log.Warn("BatchPoster: not in sync", "sequencedPosted", b.sequencesPosted) <-time.After(b.config.SubmissionSyncDelay) @@ -297,14 +298,14 @@ func (b *BatchPoster) postSequencerBatch() error { var err error prevBatchMeta, err = b.inbox.GetBatchMetadata(b.sequencesPosted - 1) if err != nil { - return err + return nil, err } } segments := newBatchSegments(prevBatchMeta.DelayedMessageCount, b.config) msgToPost := prevBatchMeta.MessageCount msgCount, err := b.streamer.GetMessageCount() if err != nil { - return err + return nil, err } for msgToPost < msgCount { msg, err := b.streamer.GetMessage(msgToPost) @@ -324,26 +325,32 @@ func (b *BatchPoster) postSequencerBatch() error { } sequencerMsg, err := segments.CloseAndGetBytes() if err != nil { - return err + return nil, err } if sequencerMsg == nil { log.Debug("BatchPoster: batch nil", "sequence nr.", b.sequencesPosted, "from", prevBatchMeta.MessageCount, "prev delayed", prevBatchMeta.DelayedMessageCount) - return nil + return nil, nil } - _, err = b.inboxContract.AddSequencerL2BatchFromOrigin(b.transactOpts, new(big.Int).SetUint64(b.sequencesPosted), sequencerMsg, new(big.Int).SetUint64(segments.delayedMsg), b.gasRefunder) + tx, err := b.inboxContract.AddSequencerL2BatchFromOrigin(b.transactOpts, new(big.Int).SetUint64(b.sequencesPosted), sequencerMsg, new(big.Int).SetUint64(segments.delayedMsg), b.gasRefunder) if err == nil { b.sequencesPosted++ log.Info("BatchPoster: batch sent", "sequence nr.", b.sequencesPosted, "from", prevBatchMeta.MessageCount, "to", msgToPost, "prev delayed", prevBatchMeta.DelayedMessageCount, "current delayed", segments.delayedMsg, "total segments", len(segments.rawSegments)) } - return err + return tx, err } func (b *BatchPoster) Start(ctx context.Context) { go (func() { for { - err := b.postSequencerBatch() + tx, err := b.postSequencerBatch() if err != nil { - log.Error("error posting batch", "err", err.Error()) + log.Error("error posting batch", "err", err) + } + if tx != nil { + _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, b.client, tx, time.Minute) + if err != nil { + log.Error("failed ensuring batch tx succeeded", "err", err) + } } select { case <-ctx.Done(): diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index d65b4ca91d..4a23f1cbe0 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -71,6 +71,12 @@ func WaitForTx(ctxinput context.Context, client L1Interface, txhash common.Hash, chanHead, cancel := HeaderSubscribeWithRetry(ctx, client) defer cancel() + checkInterval := timeout / 5 + if checkInterval > time.Second { + checkInterval = time.Second + } + timer := time.NewTimer(checkInterval) + defer timer.Stop() for { receipt, err := client.TransactionReceipt(ctx, txhash) if err == nil && receipt != nil { @@ -87,9 +93,10 @@ func WaitForTx(ctxinput context.Context, client L1Interface, txhash common.Hash, return receipt, nil } } + timer.Reset(checkInterval) select { case <-chanHead: - case <-time.After(timeout / 5): + case <-timer.C: case <-ctx.Done(): return nil, ctx.Err() } From db8f60f6351553006b68f01654e79fd20e94d9f3 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 12:25:03 -0600 Subject: [PATCH 034/110] Create blocks synchronously when sequencing delayed messages --- arbnode/delayed_sequencer.go | 2 +- arbnode/transaction_streamer.go | 46 +++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/arbnode/delayed_sequencer.go b/arbnode/delayed_sequencer.go index 92571f94e5..d534086ad8 100644 --- a/arbnode/delayed_sequencer.go +++ b/arbnode/delayed_sequencer.go @@ -126,7 +126,7 @@ func (d *DelayedSequencer) update(ctx context.Context) error { return errors.New("inbox reader db accumulator doesn't match delayed bridge") } - err = d.txStreamer.SequenceDelayedMessages(messages, startPos) + err = d.txStreamer.SequenceDelayedMessages(ctx, messages, startPos) if err != nil { return err } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 361f03fbc7..0e45a3ad27 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -365,18 +365,6 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa return err } - var logs []*types.Log - for _, receipt := range receipts { - logs = append(logs, receipt.Logs...) - } - status, err := s.bc.WriteBlockWithState(block, receipts, logs, statedb, true) - if err != nil { - return err - } - if status == core.SideStatTy { - return errors.New("geth rejected block as non-canonical") - } - msgWithMeta := arbstate.MessageWithMetadata{ Message: msg, DelayedMessagesRead: delayedMessagesRead, @@ -394,10 +382,22 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa s.broadcastServer.BroadcastSingle(msgWithMeta, pos) } + var logs []*types.Log + for _, receipt := range receipts { + logs = append(logs, receipt.Logs...) + } + status, err := s.bc.WriteBlockWithState(block, receipts, logs, statedb, true) + if err != nil { + return err + } + if status == core.SideStatTy { + return errors.New("geth rejected block as non-canonical") + } + return nil } -func (s *TransactionStreamer) SequenceDelayedMessages(messages []*arbos.L1IncomingMessage, firstDelayedSeqNum uint64) error { +func (s *TransactionStreamer) SequenceDelayedMessages(ctx context.Context, messages []*arbos.L1IncomingMessage, firstDelayedSeqNum uint64) error { s.insertionMutex.Lock() defer s.insertionMutex.Unlock() @@ -427,7 +427,25 @@ func (s *TransactionStreamer) SequenceDelayedMessages(messages []*arbos.L1Incomi }) } log.Info("TransactionStreamer: Added DelayedMessages", "pos", pos, "length", len(messages)) - return s.writeMessages(pos, messagesWithMeta, nil) + err = s.writeMessages(pos, messagesWithMeta, nil) + if err != nil { + return err + } + + genesisBlock, err := s.GetGenesisBlockNumber() + if err != nil { + return err + } + + // If we were already caught up to the latest message, ensure we produce blocks for the delayed messages. + if s.bc.CurrentHeader().Number.Uint64()+1-genesisBlock >= pos { + err = s.createBlocks(ctx) + if err != nil { + return err + } + } + + return nil } func (s *TransactionStreamer) GetGenesisBlockNumber() (uint64, error) { From c3121ef7ffaf9da6af6ea941505a0fbbb65942a3 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 12:55:02 -0600 Subject: [PATCH 035/110] Add helper functions for converting between block nums and msg counts --- arbnode/transaction_streamer.go | 35 +++++++++++++----- arbutil/block_message_relation.go | 18 +++++++++ validator/block_challenge_backend.go | 3 +- validator/block_validator.go | 4 +- validator/challenge_manager.go | 3 +- validator/l1_validator.go | 55 ++++++++++++++++------------ 6 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 arbutil/block_message_relation.go diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 0e45a3ad27..567dfcea08 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/broadcaster" "github.com/offchainlabs/arbstate/validator" ) @@ -128,11 +129,12 @@ func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count uint64) e s.reorgMutex.Lock() defer s.reorgMutex.Unlock() atomic.AddUint32(&s.reorgPending, ^uint32(0)) // decrement - genesisBlock, err := s.GetGenesisBlockNumber() + blockNum, err := s.MessageCountToBlockNumber(count) if err != nil { return err } - targetBlock := s.bc.GetBlockByNumber(count + genesisBlock - 1) + // We can safely cast blockNum to a uint64 as we checked count == 0 above + targetBlock := s.bc.GetBlockByNumber(uint64(blockNum)) if targetBlock == nil { return errors.New("reorg target block not found") } @@ -324,12 +326,12 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa if lastBlockHeader == nil { return errors.New("current block header not found") } - genesisBlock, err := s.GetGenesisBlockNumber() + expectedBlockNum, err := s.MessageCountToBlockNumber(pos) if err != nil { return err } - if lastBlockHeader.Number.Uint64()+1-genesisBlock != pos { - return fmt.Errorf("block production not caught up: last block number %v but expected %v", lastBlockHeader.Number, int64(pos)-1+int64(genesisBlock)) + if lastBlockHeader.Number.Int64() != expectedBlockNum { + return fmt.Errorf("block production not caught up: last block number %v but expected %v", lastBlockHeader.Number, expectedBlockNum) } statedb, err := s.bc.StateAt(lastBlockHeader.Root) if err != nil { @@ -432,13 +434,13 @@ func (s *TransactionStreamer) SequenceDelayedMessages(ctx context.Context, messa return err } - genesisBlock, err := s.GetGenesisBlockNumber() + expectedBlockNum, err := s.MessageCountToBlockNumber(pos) if err != nil { return err } // If we were already caught up to the latest message, ensure we produce blocks for the delayed messages. - if s.bc.CurrentHeader().Number.Uint64()+1-genesisBlock >= pos { + if s.bc.CurrentHeader().Number.Int64() >= expectedBlockNum { err = s.createBlocks(ctx) if err != nil { return err @@ -453,6 +455,22 @@ func (s *TransactionStreamer) GetGenesisBlockNumber() (uint64, error) { return 0, nil } +func (s *TransactionStreamer) BlockNumberToMessageCount(blockNum uint64) (uint64, error) { + genesis, err := s.GetGenesisBlockNumber() + if err != nil { + return 0, err + } + return arbutil.BlockNumberToMessageCount(blockNum, genesis), nil +} + +func (s *TransactionStreamer) MessageCountToBlockNumber(messageNum uint64) (int64, error) { + genesis, err := s.GetGenesisBlockNumber() + if err != nil { + return 0, err + } + return arbutil.MessageCountToBlockNumber(messageNum, genesis), nil +} + // The mutex must be held, and pos must be the latest message count. // `batch` may be nil, which initializes a new batch. The batch is closed out in this function. func (s *TransactionStreamer) writeMessages(pos uint64, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { @@ -504,11 +522,10 @@ func (s *TransactionStreamer) createBlocks(ctx context.Context) error { if lastBlockHeader == nil { return errors.New("current block header not found") } - genesisBlock, err := s.GetGenesisBlockNumber() + pos, err := s.BlockNumberToMessageCount(lastBlockHeader.Number.Uint64()) if err != nil { return err } - pos := lastBlockHeader.Number.Uint64() + 1 - genesisBlock statedb, err := s.bc.StateAt(lastBlockHeader.Root) if err != nil { return err diff --git a/arbutil/block_message_relation.go b/arbutil/block_message_relation.go new file mode 100644 index 0000000000..4e57aee320 --- /dev/null +++ b/arbutil/block_message_relation.go @@ -0,0 +1,18 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package arbutil + +func BlockNumberToMessageCount(blockNumber uint64, genesisBlockNumber uint64) uint64 { + return blockNumber + 1 - genesisBlockNumber +} + +// Block number must correspond to a message count, meaning it may not be less than -1 +func SignedBlockNumberToMessageCount(blockNumber int64, genesisBlockNumber uint64) uint64 { + return uint64(blockNumber+1) - genesisBlockNumber +} + +func MessageCountToBlockNumber(messageCount uint64, genesisBlockNumber uint64) int64 { + return int64(messageCount+genesisBlockNumber) - 1 +} diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 439fd15b60..265a1dba1e 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/offchainlabs/arbstate/arbos" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/pkg/errors" @@ -107,7 +108,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, errors.Wrap(err, "failed to get challenge start batch metadata") } } - expectedMsgCount := uint64(startBlockNum+1) - genesisBlockNum + expectedMsgCount := arbutil.SignedBlockNumberToMessageCount(startBlockNum, genesisBlockNum) if startMsgCount != expectedMsgCount { return nil, errors.New("start block and start message count are not 1:1") } diff --git a/validator/block_validator.go b/validator/block_validator.go index 2642dc4363..5d3b16b9b3 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/pkg/errors" ) @@ -498,7 +499,8 @@ func (v *BlockValidator) sendValidations(ctx context.Context) { return } // valdationEntries is By blockNumber - blockNum := v.posNextSend + v.genesisBlockNum + // TODO: casting to a uint64 here assumes that we aren't validating the genesis block + blockNum := uint64(arbutil.MessageCountToBlockNumber(v.posNextSend, v.genesisBlockNum)) entry, found := v.validationEntries.Load(blockNum) if !found { return diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index df85baaeed..8882af2a2a 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/pkg/errors" ) @@ -333,7 +334,7 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in if err != nil { return err } - message, err := m.txStreamer.GetMessage(uint64(blockNum+1) - genesisBlockNum) + message, err := m.txStreamer.GetMessage(arbutil.SignedBlockNumberToMessageCount(blockNum, genesisBlockNum)) if err != nil { return err } diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 078457ce20..91875e282f 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -216,7 +216,7 @@ type OurStakerInfo struct { // Returns (block number, global state inbox position is invalid, error). // If global state is invalid, block number is set to the last of the batch. -func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (uint64, bool, error) { +func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (int64, bool, error) { var batchHeight uint64 if gs.Batch > 0 { var err error @@ -233,10 +233,11 @@ func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (uint64, bool, if gs.PosInBatch >= nextBatchHeight-batchHeight { // This PosInBatch would enter the next batch. Return the last block before the next batch. - return v.genesisBlockNumber + nextBatchHeight - 1, true, nil + // We can be sure that MessageCountToBlockNumber will return a non-negative number as nextBatchHeight must be nonzero. + return arbutil.MessageCountToBlockNumber(nextBatchHeight, v.genesisBlockNumber), true, nil } - return v.genesisBlockNumber + batchHeight + gs.PosInBatch, false, nil + return arbutil.MessageCountToBlockNumber(batchHeight+gs.PosInBatch, v.genesisBlockNumber), false, nil } func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy) (nodeAction, bool, error) { @@ -265,7 +266,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return nil, false, errors.New("invalid start global state inbox position") } latestHeader := v.l2Blockchain.CurrentHeader() - if latestHeader.Number.Uint64() < expectedBlockHeight { + if latestHeader.Number.Int64() < expectedBlockHeight { log.Info("catching up to chain blocks", "localBlocks", latestHeader.Number, "target", expectedBlockHeight) return nil, false, nil } else { @@ -285,7 +286,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake if err != nil { return nil, false, err } - lastBatchBlock := v.genesisBlockNumber + messageCount + // Must be non-negative as a batch must contain at least one message + lastBatchBlock := uint64(arbutil.MessageCountToBlockNumber(messageCount, v.genesisBlockNumber)) if blocksValidated > lastBatchBlock { blocksValidated = lastBatchBlock } @@ -345,28 +347,34 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake if err != nil { return nil, false, err } - if blocksValidated < lastBlockNum { + if int64(blocksValidated) < lastBlockNum { return nil, false, fmt.Errorf("waiting for validator to catch up to assertion blocks: %v/%v", blocksValidated, lastBlockNum) } - lastBlock := v.l2Blockchain.GetBlockByNumber(lastBlockNum) - if lastBlock == nil { - return nil, false, fmt.Errorf("block %v not in database despite being validated", lastBlockNum) - } - lastBlockExtra, err := arbos.DeserializeHeaderExtraInformation(lastBlock.Header()) - if err != nil { - return nil, false, err + var expectedBlockHash common.Hash + var expectedSendRoot common.Hash + if lastBlockNum >= 0 { + lastBlock := v.l2Blockchain.GetBlockByNumber(uint64(lastBlockNum)) + if lastBlock == nil { + return nil, false, fmt.Errorf("block %v not in database despite being validated", lastBlockNum) + } + lastBlockExtra, err := arbos.DeserializeHeaderExtraInformation(lastBlock.Header()) + if err != nil { + return nil, false, err + } + expectedBlockHash = lastBlock.Hash() + expectedSendRoot = lastBlockExtra.SendRoot } var expectedNumBlocks uint64 if startBlock == nil { - expectedNumBlocks = lastBlockNum + 1 + expectedNumBlocks = uint64(lastBlockNum + 1) } else { - expectedNumBlocks = lastBlockNum - startBlock.NumberU64() + expectedNumBlocks = uint64(lastBlockNum) - startBlock.NumberU64() } valid := !inboxPositionInvalid && nd.Assertion.NumBlocks == expectedNumBlocks && - afterGs.BlockHash == lastBlock.Hash() && - afterGs.SendRoot == lastBlockExtra.SendRoot + afterGs.BlockHash == expectedBlockHash && + afterGs.SendRoot == expectedSendRoot if valid { log.Info( "found correct node", @@ -388,9 +396,9 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake "numBlocks", nd.Assertion.NumBlocks, "expectedNumBlocks", expectedNumBlocks, "blockHash", afterGs.BlockHash, - "expectedBlockHash", lastBlock.Hash(), + "expectedBlockHash", expectedBlockHash, "sendRoot", afterGs.SendRoot, - "expectedSendRoot", lastBlockExtra.SendRoot, + "expectedSendRoot", expectedSendRoot, ) } } else { @@ -426,16 +434,17 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake var afterGsBatch uint64 var afterGsPosInBatch uint64 for i := localBatchCount - 1; i+1 >= minBatchCount && i > 0; i-- { - lastBlockNum, err := v.inboxTracker.GetBatchMessageCount(i) + batchMessageCount, err := v.inboxTracker.GetBatchMessageCount(i) if err != nil { return nil, false, err } - lastBlockNum += v.genesisBlockNumber - prevBlockNum, err := v.inboxTracker.GetBatchMessageCount(i - 1) + prevBatchMessageCount, err := v.inboxTracker.GetBatchMessageCount(i - 1) if err != nil { return nil, false, err } - prevBlockNum += v.genesisBlockNumber + // Must be non-negative as a batch must contain at least one message + lastBlockNum := uint64(arbutil.MessageCountToBlockNumber(batchMessageCount, v.genesisBlockNumber)) + prevBlockNum := uint64(arbutil.MessageCountToBlockNumber(prevBatchMessageCount, v.genesisBlockNumber)) if lastBlockValidated > prevBlockNum && lastBlockValidated <= lastBlockNum { // We found the batch containing the last validated block if i+1 == minBatchCount && lastBlockValidated < lastBlockNum { From 4ae2f47142162af4fc6b8942d96c09408a9a2dea Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 13:00:02 -0600 Subject: [PATCH 036/110] Fix block validator block num calculation --- validator/block_validator.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/validator/block_validator.go b/validator/block_validator.go index 5d3b16b9b3..d41147e429 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -499,9 +499,8 @@ func (v *BlockValidator) sendValidations(ctx context.Context) { return } // valdationEntries is By blockNumber - // TODO: casting to a uint64 here assumes that we aren't validating the genesis block - blockNum := uint64(arbutil.MessageCountToBlockNumber(v.posNextSend, v.genesisBlockNum)) - entry, found := v.validationEntries.Load(blockNum) + nextBlockNum := uint64(arbutil.MessageCountToBlockNumber(v.posNextSend, v.genesisBlockNum) + 1) + entry, found := v.validationEntries.Load(nextBlockNum) if !found { return } From 74c3a7eefd43e3f262b23a626d028e62286e6ee8 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 13:10:29 -0600 Subject: [PATCH 037/110] Use MessageCountToBlockNumber in one more place --- validator/block_challenge_backend.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 265a1dba1e..0f89bb6a29 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -92,7 +92,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra if startGs.PosInBatch != 0 { return nil, errors.New("challenge started misaligned with batch boundary") } - startBlockNum := int64(genesisBlockNum) - 1 + startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNum) if startGs.BlockHash != (common.Hash{}) { startBlock := bc.GetBlockByHash(startGs.BlockHash) if startBlock == nil { From 0d3e4c5cb7b20041b89c157385bcf4e6606b5539 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 15:38:06 -0600 Subject: [PATCH 038/110] Use a separate type for message indicies --- arbnode/inbox_test.go | 5 ++-- arbnode/inbox_tracker.go | 9 ++++--- arbnode/transaction_streamer.go | 36 ++++++++++++------------- arbutil/block_message_relation.go | 14 +++++----- broadcastclient/broadcastclient.go | 5 ++-- broadcastclient/broadcastclient_test.go | 7 ++--- broadcaster/broadcaster.go | 11 ++++---- validator/block_challenge_backend.go | 20 +++++++------- validator/block_validator.go | 14 +++++----- validator/l1_validator.go | 6 ++--- 10 files changed, 68 insertions(+), 59 deletions(-) diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index 3ce0ff5f65..fb31a66b8a 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/offchainlabs/arbstate/arbos/l2pricing" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/statetransfer" "github.com/offchainlabs/arbstate/arbos/util" @@ -75,7 +76,7 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* type blockTestState struct { balances map[common.Address]*big.Int accounts []common.Address - numMessages uint64 + numMessages arbutil.MessageIndex blockNumber uint64 } @@ -161,7 +162,7 @@ func TestTransactionStreamer(t *testing.T) { Require(t, inbox.AddMessages(state.numMessages, false, messages)) - state.numMessages += uint64(len(messages)) + state.numMessages += arbutil.MessageIndex(len(messages)) state.blockNumber += uint64(len(messages)) for i := 0; ; i++ { blockNumber := bc.CurrentHeader().Number.Uint64() diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index dce7859293..e87c5c726d 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/rlp" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/validator" "github.com/pkg/errors" ) @@ -115,7 +116,7 @@ func (t *InboxTracker) GetDelayedCount() (uint64, error) { type BatchMetadata struct { Accumulator common.Hash - MessageCount uint64 + MessageCount arbutil.MessageIndex DelayedMessageCount uint64 L1Block uint64 } @@ -138,7 +139,7 @@ func (t *InboxTracker) GetBatchMetadata(seqNum uint64) (BatchMetadata, error) { return metadata, err } -func (t *InboxTracker) GetBatchMessageCount(seqNum uint64) (uint64, error) { +func (t *InboxTracker) GetBatchMessageCount(seqNum uint64) (arbutil.MessageIndex, error) { metadata, err := t.GetBatchMetadata(seqNum) return metadata.MessageCount, err } @@ -315,7 +316,7 @@ func (t *InboxTracker) setDelayedCountReorgAndWriteBatch(batch ethdb.Batch, newD if err != nil { return err } - var prevMesssageCount uint64 + var prevMesssageCount arbutil.MessageIndex if count > 0 { prevMesssageCount, err = t.GetBatchMessageCount(count - 1) if err != nil { @@ -437,7 +438,7 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client ethereum. client: client, } multiplexer := arbstate.NewInboxMultiplexer(backend, prevbatchmeta.DelayedMessageCount) - batchMessageCounts := make(map[uint64]uint64) + batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentpos := prevbatchmeta.MessageCount + 1 for { if len(backend.batches) == 0 { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 567dfcea08..72c8acc0a7 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -85,11 +85,11 @@ func (s *TransactionStreamer) cleanupInconsistentState() error { return nil } -func (s *TransactionStreamer) ReorgTo(count uint64) error { +func (s *TransactionStreamer) ReorgTo(count arbutil.MessageIndex) error { return s.ReorgToAndEndBatch(s.db.NewBatch(), count) } -func (s *TransactionStreamer) ReorgToAndEndBatch(batch ethdb.Batch, count uint64) error { +func (s *TransactionStreamer) ReorgToAndEndBatch(batch ethdb.Batch, count arbutil.MessageIndex) error { s.insertionMutex.Lock() defer s.insertionMutex.Unlock() err := s.reorgToInternal(batch, count) @@ -121,7 +121,7 @@ func deleteStartingAt(db ethdb.Database, batch ethdb.Batch, prefix []byte, minKe return nil } -func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count uint64) error { +func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count arbutil.MessageIndex) error { if count == 0 { return errors.New("cannot reorg out init message") } @@ -144,7 +144,7 @@ func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count uint64) e return err } - err = deleteStartingAt(s.db, batch, messagePrefix, uint64ToBytes(count)) + err = deleteStartingAt(s.db, batch, messagePrefix, uint64ToBytes(uint64(count))) if err != nil { return err } @@ -163,13 +163,13 @@ func (s *TransactionStreamer) reorgToInternal(batch ethdb.Batch, count uint64) e func dbKey(prefix []byte, pos uint64) []byte { var key []byte key = append(key, prefix...) - key = append(key, uint64ToBytes(pos)...) + key = append(key, uint64ToBytes(uint64(pos))...) return key } // Note: if changed to acquire the mutex, some internal users may need to be updated to a non-locking version. -func (s *TransactionStreamer) GetMessage(seqNum uint64) (arbstate.MessageWithMetadata, error) { - key := dbKey(messagePrefix, seqNum) +func (s *TransactionStreamer) GetMessage(seqNum arbutil.MessageIndex) (arbstate.MessageWithMetadata, error) { + key := dbKey(messagePrefix, uint64(seqNum)) data, err := s.db.Get(key) if err != nil { return arbstate.MessageWithMetadata{}, err @@ -180,7 +180,7 @@ func (s *TransactionStreamer) GetMessage(seqNum uint64) (arbstate.MessageWithMet } // Note: if changed to acquire the mutex, some internal users may need to be updated to a non-locking version. -func (s *TransactionStreamer) GetMessageCount() (uint64, error) { +func (s *TransactionStreamer) GetMessageCount() (arbutil.MessageIndex, error) { posBytes, err := s.db.Get(messageCountKey) if err != nil { return 0, err @@ -190,19 +190,19 @@ func (s *TransactionStreamer) GetMessageCount() (uint64, error) { if err != nil { return 0, err } - return pos, nil + return arbutil.MessageIndex(pos), nil } -func (s *TransactionStreamer) AddMessages(pos uint64, force bool, messages []arbstate.MessageWithMetadata) error { +func (s *TransactionStreamer) AddMessages(pos arbutil.MessageIndex, force bool, messages []arbstate.MessageWithMetadata) error { return s.AddMessagesAndEndBatch(pos, force, messages, nil) } -func (s *TransactionStreamer) AddMessagesAndEndBatch(pos uint64, force bool, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { +func (s *TransactionStreamer) AddMessagesAndEndBatch(pos arbutil.MessageIndex, force bool, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { s.insertionMutex.Lock() defer s.insertionMutex.Unlock() if pos > 0 { - key := dbKey(messagePrefix, pos-1) + key := dbKey(messagePrefix, uint64(pos-1)) hasPrev, err := s.db.Has(key) if err != nil { return err @@ -218,7 +218,7 @@ func (s *TransactionStreamer) AddMessagesAndEndBatch(pos uint64, force bool, mes if len(messages) == 0 { break } - key := dbKey(messagePrefix, pos) + key := dbKey(messagePrefix, uint64(pos)) hasMessage, err := s.db.Has(key) if err != nil { return err @@ -455,7 +455,7 @@ func (s *TransactionStreamer) GetGenesisBlockNumber() (uint64, error) { return 0, nil } -func (s *TransactionStreamer) BlockNumberToMessageCount(blockNum uint64) (uint64, error) { +func (s *TransactionStreamer) BlockNumberToMessageCount(blockNum uint64) (arbutil.MessageIndex, error) { genesis, err := s.GetGenesisBlockNumber() if err != nil { return 0, err @@ -463,7 +463,7 @@ func (s *TransactionStreamer) BlockNumberToMessageCount(blockNum uint64) (uint64 return arbutil.BlockNumberToMessageCount(blockNum, genesis), nil } -func (s *TransactionStreamer) MessageCountToBlockNumber(messageNum uint64) (int64, error) { +func (s *TransactionStreamer) MessageCountToBlockNumber(messageNum arbutil.MessageIndex) (int64, error) { genesis, err := s.GetGenesisBlockNumber() if err != nil { return 0, err @@ -473,12 +473,12 @@ func (s *TransactionStreamer) MessageCountToBlockNumber(messageNum uint64) (int6 // The mutex must be held, and pos must be the latest message count. // `batch` may be nil, which initializes a new batch. The batch is closed out in this function. -func (s *TransactionStreamer) writeMessages(pos uint64, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { +func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages []arbstate.MessageWithMetadata, batch ethdb.Batch) error { if batch == nil { batch = s.db.NewBatch() } for i, msg := range messages { - key := dbKey(messagePrefix, pos+uint64(i)) + key := dbKey(messagePrefix, uint64(pos)+uint64(i)) msgBytes, err := rlp.EncodeToBytes(msg) if err != nil { return err @@ -488,7 +488,7 @@ func (s *TransactionStreamer) writeMessages(pos uint64, messages []arbstate.Mess return err } } - newCount, err := rlp.EncodeToBytes(pos + uint64(len(messages))) + newCount, err := rlp.EncodeToBytes(uint64(pos) + uint64(len(messages))) if err != nil { return err } diff --git a/arbutil/block_message_relation.go b/arbutil/block_message_relation.go index 4e57aee320..898bffcb31 100644 --- a/arbutil/block_message_relation.go +++ b/arbutil/block_message_relation.go @@ -4,15 +4,17 @@ package arbutil -func BlockNumberToMessageCount(blockNumber uint64, genesisBlockNumber uint64) uint64 { - return blockNumber + 1 - genesisBlockNumber +type MessageIndex uint64 + +func BlockNumberToMessageCount(blockNumber uint64, genesisBlockNumber uint64) MessageIndex { + return MessageIndex(blockNumber + 1 - genesisBlockNumber) } // Block number must correspond to a message count, meaning it may not be less than -1 -func SignedBlockNumberToMessageCount(blockNumber int64, genesisBlockNumber uint64) uint64 { - return uint64(blockNumber+1) - genesisBlockNumber +func SignedBlockNumberToMessageCount(blockNumber int64, genesisBlockNumber uint64) MessageIndex { + return MessageIndex(uint64(blockNumber+1) - genesisBlockNumber) } -func MessageCountToBlockNumber(messageCount uint64, genesisBlockNumber uint64) int64 { - return int64(messageCount+genesisBlockNumber) - 1 +func MessageCountToBlockNumber(messageCount MessageIndex, genesisBlockNumber uint64) int64 { + return int64(uint64(messageCount)+genesisBlockNumber) - 1 } diff --git a/broadcastclient/broadcastclient.go b/broadcastclient/broadcastclient.go index e2e3970a66..809feaf94d 100644 --- a/broadcastclient/broadcastclient.go +++ b/broadcastclient/broadcastclient.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/broadcaster" "github.com/offchainlabs/arbstate/wsbroadcastserver" ) @@ -31,7 +32,7 @@ type BroadcastClientConfig struct { var DefaultBroadcastClientConfig BroadcastClientConfig type TransactionStreamerInterface interface { - AddMessages(pos uint64, force bool, messages []arbstate.MessageWithMetadata) error + AddMessages(pos arbutil.MessageIndex, force bool, messages []arbstate.MessageWithMetadata) error } type BroadcastClient struct { @@ -46,7 +47,7 @@ type BroadcastClient struct { retrying bool shuttingDown bool - ConfirmedSequenceNumberListener chan uint64 + ConfirmedSequenceNumberListener chan arbutil.MessageIndex idleTimeout time.Duration txStreamer TransactionStreamerInterface } diff --git a/broadcastclient/broadcastclient_test.go b/broadcastclient/broadcastclient_test.go index bcfb0852fb..5adcaf9db2 100644 --- a/broadcastclient/broadcastclient_test.go +++ b/broadcastclient/broadcastclient_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/broadcaster" "github.com/offchainlabs/arbstate/wsbroadcastserver" ) @@ -47,7 +48,7 @@ func TestReceiveMessages(t *testing.T) { go func() { for i := 0; i < messageCount; i++ { - b.BroadcastSingle(arbstate.MessageWithMetadata{}, uint64(i)) + b.BroadcastSingle(arbstate.MessageWithMetadata{}, arbutil.MessageIndex(i)) } }() @@ -65,10 +66,10 @@ func NewDummyTransactionStreamer() *dummyTransactionStreamer { } } -func (ts *dummyTransactionStreamer) AddMessages(pos uint64, force bool, messages []arbstate.MessageWithMetadata) error { +func (ts *dummyTransactionStreamer) AddMessages(pos arbutil.MessageIndex, force bool, messages []arbstate.MessageWithMetadata) error { for i, message := range messages { ts.messageReceiver <- broadcaster.BroadcastFeedMessage{ - SequenceNumber: pos + uint64(i), + SequenceNumber: pos + arbutil.MessageIndex(i), Message: message, } } diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index 5e90a000b0..91c7c0c295 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbstate" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/wsbroadcastserver" ) @@ -44,12 +45,12 @@ type BroadcastMessage struct { } type BroadcastFeedMessage struct { - SequenceNumber uint64 `json:"sequenceNumber"` + SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"` Message arbstate.MessageWithMetadata `json:"message"` } type ConfirmedSequenceNumberMessage struct { - SequenceNumber uint64 `json:"sequenceNumber"` + SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"` } type SequenceNumberCatchupBuffer struct { @@ -104,7 +105,7 @@ func (b *SequenceNumberCatchupBuffer) OnDoBroadcast(bmi interface{}) error { if confirmMsg.SequenceNumber < b.messages[0].SequenceNumber { return nil } - confirmedIndex := confirmMsg.SequenceNumber - b.messages[0].SequenceNumber + confirmedIndex := uint64(confirmMsg.SequenceNumber - b.messages[0].SequenceNumber) if uint64(len(b.messages)) <= confirmedIndex { log.Error("ConfirmedSequenceNumber message ", confirmMsg.SequenceNumber, " is past the end of stored messages. Clearing buffer. Final stored sequence number was ", b.messages[len(b.messages)-1]) @@ -158,7 +159,7 @@ func NewBroadcaster(settings wsbroadcastserver.BroadcasterConfig) *Broadcaster { } } -func (b *Broadcaster) BroadcastSingle(msg arbstate.MessageWithMetadata, seq uint64) { +func (b *Broadcaster) BroadcastSingle(msg arbstate.MessageWithMetadata, seq arbutil.MessageIndex) { var broadcastMessages []*BroadcastFeedMessage bfm := BroadcastFeedMessage{SequenceNumber: seq, Message: msg} @@ -172,7 +173,7 @@ func (b *Broadcaster) BroadcastSingle(msg arbstate.MessageWithMetadata, seq uint b.server.Broadcast(bm) } -func (b *Broadcaster) Confirm(seq uint64) { +func (b *Broadcaster) Confirm(seq arbutil.MessageIndex) { b.server.Broadcast(BroadcastMessage{ Version: 1, ConfirmedSequenceNumberMessage: &ConfirmedSequenceNumberMessage{seq}}) diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 0f89bb6a29..4e92e190bf 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -71,13 +71,14 @@ type BlockChallengeBackend struct { startGs GoGlobalState endGs GoGlobalState inboxTracker InboxTrackerInterface + genesisBlockNumber uint64 tooFarStartsAtPosition uint64 } // Assert that BlockChallengeBackend implements ChallengeBackend var _ ChallengeBackend = (*BlockChallengeBackend)(nil) -func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNum uint64) (*BlockChallengeBackend, error) { +func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNumber uint64) (*BlockChallengeBackend, error) { callOpts := &bind.CallOpts{Context: ctx} challengeCon, err := challengegen.NewBlockChallenge(challengeAddr, client) if err != nil { @@ -92,7 +93,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra if startGs.PosInBatch != 0 { return nil, errors.New("challenge started misaligned with batch boundary") } - startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNum) + startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNumber) if startGs.BlockHash != (common.Hash{}) { startBlock := bc.GetBlockByHash(startGs.BlockHash) if startBlock == nil { @@ -101,14 +102,14 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra startBlockNum = int64(startBlock.NumberU64()) } - var startMsgCount uint64 + var startMsgCount arbutil.MessageIndex if startGs.Batch > 0 { startMsgCount, err = inboxTracker.GetBatchMessageCount(startGs.Batch - 1) if err != nil { return nil, errors.Wrap(err, "failed to get challenge start batch metadata") } } - expectedMsgCount := arbutil.SignedBlockNumberToMessageCount(startBlockNum, genesisBlockNum) + expectedMsgCount := arbutil.SignedBlockNumberToMessageCount(startBlockNum, genesisBlockNumber) if startMsgCount != expectedMsgCount { return nil, errors.New("start block and start message count are not 1:1") } @@ -139,11 +140,12 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra endPosition: math.MaxUint64, endGs: endGs, inboxTracker: inboxTracker, - tooFarStartsAtPosition: endMsgCount - startMsgCount + 1, + genesisBlockNumber: genesisBlockNumber, + tooFarStartsAtPosition: uint64(endMsgCount - startMsgCount + 1), }, nil } -func (b *BlockChallengeBackend) findBatchFromMessageCount(ctx context.Context, msgCount uint64) (uint64, error) { +func (b *BlockChallengeBackend) findBatchFromMessageCount(ctx context.Context, msgCount arbutil.MessageIndex) (uint64, error) { if msgCount == 0 { return 0, nil } @@ -180,12 +182,12 @@ func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, h if header == nil { return GoGlobalState{}, nil } - msgCount := header.Number.Uint64() + msgCount := arbutil.BlockNumberToMessageCount(header.Number.Uint64(), b.genesisBlockNumber) batch, err := b.findBatchFromMessageCount(ctx, msgCount) if err != nil { return GoGlobalState{}, err } - var batchMsgCount uint64 + var batchMsgCount arbutil.MessageIndex if batch > 0 { batchMsgCount, err = b.inboxTracker.GetBatchMessageCount(batch - 1) if err != nil { @@ -199,7 +201,7 @@ func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, h if err != nil { return GoGlobalState{}, err } - return GoGlobalState{header.Hash(), extraInfo.SendRoot, batch, msgCount - batchMsgCount}, nil + return GoGlobalState{header.Hash(), extraInfo.SendRoot, batch, uint64(msgCount - batchMsgCount)}, nil } const STATUS_FINISHED uint8 = 1 diff --git a/validator/block_validator.go b/validator/block_validator.go index d41147e429..660378fa81 100644 --- a/validator/block_validator.go +++ b/validator/block_validator.go @@ -38,7 +38,7 @@ type BlockValidator struct { blocksValidated uint64 earliestBatchKept uint64 - posNextSend uint64 + posNextSend arbutil.MessageIndex globalPosNextSend GlobalStatePosition config *BlockValidatorConfig @@ -69,14 +69,14 @@ type BlockValidatorRegistrer interface { type InboxTrackerInterface interface { BlockValidatorRegistrer GetDelayedMessageBytes(uint64) ([]byte, error) - GetBatchMessageCount(seqNum uint64) (uint64, error) + GetBatchMessageCount(seqNum uint64) (arbutil.MessageIndex, error) GetBatchAcc(seqNum uint64) (common.Hash, error) GetBatchCount() (uint64, error) } type TransactionStreamerInterface interface { BlockValidatorRegistrer - GetMessage(seqNum uint64) (arbstate.MessageWithMetadata, error) + GetMessage(seqNum arbutil.MessageIndex) (arbstate.MessageWithMetadata, error) GetGenesisBlockNumber() (uint64, error) } @@ -89,12 +89,12 @@ type GlobalStatePosition struct { PosInBatch uint64 } -func GlobalStatePositionsFor(reader InboxTrackerInterface, pos uint64, batch uint64) (GlobalStatePosition, GlobalStatePosition, error) { +func GlobalStatePositionsFor(reader InboxTrackerInterface, pos arbutil.MessageIndex, batch uint64) (GlobalStatePosition, GlobalStatePosition, error) { msgCountInBatch, err := reader.GetBatchMessageCount(batch) if err != nil { return GlobalStatePosition{}, GlobalStatePosition{}, err } - var firstInBatch uint64 + var firstInBatch arbutil.MessageIndex if batch > 0 { firstInBatch, err = reader.GetBatchMessageCount(batch - 1) if err != nil { @@ -107,11 +107,11 @@ func GlobalStatePositionsFor(reader InboxTrackerInterface, pos uint64, batch uin if firstInBatch > pos { return GlobalStatePosition{}, GlobalStatePosition{}, fmt.Errorf("batch %d starts from %d, failed getting for %d", batch, firstInBatch, pos) } - startPos := GlobalStatePosition{batch, pos - firstInBatch} + startPos := GlobalStatePosition{batch, uint64(pos - firstInBatch)} if msgCountInBatch == pos+1 { return startPos, GlobalStatePosition{batch + 1, 0}, nil } - return startPos, GlobalStatePosition{batch, pos + 1 - firstInBatch}, nil + return startPos, GlobalStatePosition{batch, uint64(pos + 1 - firstInBatch)}, nil } type validationEntry struct { diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 91875e282f..de2a0b7fa3 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -217,7 +217,7 @@ type OurStakerInfo struct { // Returns (block number, global state inbox position is invalid, error). // If global state is invalid, block number is set to the last of the batch. func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (int64, bool, error) { - var batchHeight uint64 + var batchHeight arbutil.MessageIndex if gs.Batch > 0 { var err error batchHeight, err = v.inboxTracker.GetBatchMessageCount(gs.Batch - 1) @@ -231,13 +231,13 @@ func (v *Validator) blockNumberFromGlobalState(gs GoGlobalState) (int64, bool, e return 0, false, err } - if gs.PosInBatch >= nextBatchHeight-batchHeight { + if gs.PosInBatch >= uint64(nextBatchHeight-batchHeight) { // This PosInBatch would enter the next batch. Return the last block before the next batch. // We can be sure that MessageCountToBlockNumber will return a non-negative number as nextBatchHeight must be nonzero. return arbutil.MessageCountToBlockNumber(nextBatchHeight, v.genesisBlockNumber), true, nil } - return arbutil.MessageCountToBlockNumber(batchHeight+gs.PosInBatch, v.genesisBlockNumber), false, nil + return arbutil.MessageCountToBlockNumber(batchHeight+arbutil.MessageIndex(gs.PosInBatch), v.genesisBlockNumber), false, nil } func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStakerInfo, strategy StakerStrategy) (nodeAction, bool, error) { From ac116a485459c166c30afc832c40e3e149e1855e Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Fri, 18 Feb 2022 16:41:00 -0600 Subject: [PATCH 039/110] Fix full challenge test --- arbnode/sequencer_inbox.go | 2 +- solgen/src/bridge/ISequencerInbox.sol | 26 +++++++++++++++++++ solgen/src/bridge/SequencerInbox.sol | 26 ------------------- solgen/src/mocks/SequencerInboxStub.sol | 33 +++++++------------------ system_tests/full_challenge_test.go | 6 ++--- system_tests/staker_test.go | 7 +++++- validator/block_challenge_backend.go | 24 ++++++++---------- validator/challenge_manager.go | 8 +++--- 8 files changed, 59 insertions(+), 73 deletions(-) diff --git a/arbnode/sequencer_inbox.go b/arbnode/sequencer_inbox.go index e73a5adb24..d96ef438d6 100644 --- a/arbnode/sequencer_inbox.go +++ b/arbnode/sequencer_inbox.go @@ -89,7 +89,7 @@ type SequencerInboxBatch struct { AfterInboxAcc common.Hash AfterDelayedAcc common.Hash AfterDelayedCount uint64 - TimeBounds bridgegen.SequencerInboxTimeBounds + TimeBounds bridgegen.ISequencerInboxTimeBounds dataIfAvailable *[]byte txIndexInBlock uint } diff --git a/solgen/src/bridge/ISequencerInbox.sol b/solgen/src/bridge/ISequencerInbox.sol index d65602813c..ae8d9d2bea 100644 --- a/solgen/src/bridge/ISequencerInbox.sol +++ b/solgen/src/bridge/ISequencerInbox.sol @@ -15,6 +15,32 @@ interface ISequencerInbox { uint256 futureSeconds; } + struct TimeBounds { + uint64 minTimestamp; + uint64 maxTimestamp; + uint64 minBlockNumber; + uint64 maxBlockNumber; + } + + event SequencerBatchDelivered( + uint256 indexed batchSequenceNumber, + bytes32 indexed beforeAcc, + bytes32 indexed afterAcc, + bytes32 delayedAcc, + uint256 afterDelayedMessagesRead, + TimeBounds timeBounds, + bytes data + ); + + event SequencerBatchDeliveredFromOrigin( + uint256 indexed batchSequenceNumber, + bytes32 indexed beforeAcc, + bytes32 indexed afterAcc, + bytes32 delayedAcc, + uint256 afterDelayedMessagesRead, + TimeBounds timeBounds + ); + function inboxAccs(uint256 index) external view returns (bytes32); function batchCount() external view returns (uint256); diff --git a/solgen/src/bridge/SequencerInbox.sol b/solgen/src/bridge/SequencerInbox.sol index 56f25e290a..3c12956d3e 100644 --- a/solgen/src/bridge/SequencerInbox.sol +++ b/solgen/src/bridge/SequencerInbox.sol @@ -33,32 +33,6 @@ contract SequencerInbox is ISequencerInbox { mapping(address => bool) public isBatchPoster; ISequencerInbox.MaxTimeVariation public maxTimeVariation; - struct TimeBounds { - uint64 minTimestamp; - uint64 maxTimestamp; - uint64 minBlockNumber; - uint64 maxBlockNumber; - } - - event SequencerBatchDelivered( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - TimeBounds timeBounds, - bytes data - ); - - event SequencerBatchDeliveredFromOrigin( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - TimeBounds timeBounds - ); - function initialize(IBridge _delayedBridge, address rollup_) external { require(delayedBridge == IBridge(address(0)), "ALREADY_INIT"); require(_delayedBridge != IBridge(address(0)), "ZERO_BRIDGE"); diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index 054d459346..c478fdbd08 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -18,25 +18,6 @@ contract SequencerInboxStub is ISequencerInbox { mapping(address => bool) public isBatchPoster; - event SequencerBatchDelivered( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - uint64[4] timeBounds, - bytes data - ); - - event SequencerBatchDeliveredFromOrigin( - uint256 indexed batchSequenceNumber, - bytes32 indexed beforeAcc, - bytes32 indexed afterAcc, - bytes32 delayedAcc, - uint256 afterDelayedMessagesRead, - uint64[4] timeBounds - ); - constructor(IBridge _delayedBridge, address _sequencer) { delayedBridge = _delayedBridge; isBatchPoster[_sequencer] = true; @@ -52,7 +33,7 @@ contract SequencerInboxStub is ISequencerInbox { bytes32 acc = keccak256(abi.encodePacked(bytes32(0), headerHash, bytes32(0))); inboxAccs.push(acc); bytes memory data; - uint64[4] memory timeBounds; + TimeBounds memory timeBounds; emit SequencerBatchDelivered(0, bytes32(0), acc, bytes32(0), 0, timeBounds, data); } @@ -78,7 +59,9 @@ contract SequencerInboxStub is ISequencerInbox { bytes32 afterAcc ) = addSequencerL2BatchImpl(data, afterDelayedMessagesRead); - uint64[4] memory emptyTimeBounds; + TimeBounds memory emptyTimeBounds; + emptyTimeBounds.maxTimestamp = ~uint64(0); + emptyTimeBounds.maxBlockNumber = ~uint64(0); emit SequencerBatchDeliveredFromOrigin( inboxAccs.length - 1, beforeAcc, @@ -103,7 +86,9 @@ contract SequencerInboxStub is ISequencerInbox { bytes32 delayedAcc, bytes32 afterAcc ) = addSequencerL2BatchImpl(data, afterDelayedMessagesRead); - uint64[4] memory emptyTimeBounds; + TimeBounds memory emptyTimeBounds; + emptyTimeBounds.maxTimestamp = ~uint64(0); + emptyTimeBounds.maxBlockNumber = ~uint64(0); emit SequencerBatchDelivered( inboxAccs.length - 1, beforeAcc, @@ -141,9 +126,9 @@ contract SequencerInboxStub is ISequencerInbox { bytes memory header = abi.encodePacked( uint64(0), + ~uint64(0), uint64(0), - uint64(0), - uint64(0), + ~uint64(0), uint64(afterDelayedMessagesRead) ); require(header.length == 40, "BAD_HEADER_LEN"); diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index ee245b7674..07f479c43d 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -236,11 +236,11 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { t.Fatal(err) } - asserterSeqInboxAddr, _, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress(("sequencer"))) + asserterSeqInboxAddr, _, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer")) if err != nil { t.Fatal(err) } - challengerSeqInboxAddr, _, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress(("sequencer"))) + challengerSeqInboxAddr, _, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer")) if err != nil { t.Fatal(err) } @@ -298,7 +298,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { asserterStartGlobalState := validator.GoGlobalState{ BlockHash: asserterGenesis.Hash(), - Batch: 0, + Batch: 1, PosInBatch: 0, } asserterEndGlobalState := validator.GoGlobalState{ diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 7a64cc9c8d..a2ffee0d77 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -151,8 +151,13 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) // Continually make L2 transactions in a background thread var stopBackgroundTxs int32 - defer (func() { atomic.StoreInt32(&stopBackgroundTxs, 1) })() + backgroundTxsShutdownChan := make(chan struct{}) + defer (func() { + atomic.StoreInt32(&stopBackgroundTxs, 1) + <-backgroundTxsShutdownChan + })() go (func() { + defer close(backgroundTxsShutdownChan) for i := uint64(0); atomic.LoadInt32(&stopBackgroundTxs) == 0; i++ { l2info.Accounts["BackgroundUser"].Nonce = i tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 4e92e190bf..0d1dc97cf1 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -90,9 +90,6 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, err } startGs := GoGlobalStateFromSolidity(solStartGs) - if startGs.PosInBatch != 0 { - return nil, errors.New("challenge started misaligned with batch boundary") - } startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNumber) if startGs.BlockHash != (common.Hash{}) { startBlock := bc.GetBlockByHash(startGs.BlockHash) @@ -109,9 +106,10 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, errors.Wrap(err, "failed to get challenge start batch metadata") } } + startMsgCount += arbutil.MessageIndex(startGs.PosInBatch) expectedMsgCount := arbutil.SignedBlockNumberToMessageCount(startBlockNum, genesisBlockNumber) if startMsgCount != expectedMsgCount { - return nil, errors.New("start block and start message count are not 1:1") + return nil, fmt.Errorf("start block %v and start message count %v don't correspond", startBlockNum, startMsgCount) } solEndGs, err := challengeCon.GetEndGlobalState(callOpts) @@ -119,16 +117,14 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, err } endGs := GoGlobalStateFromSolidity(solEndGs) - if endGs.PosInBatch != 0 { - return nil, errors.New("challenge ended misaligned with batch boundary") - } - if endGs.Batch <= startGs.Batch { - return nil, errors.New("challenge didn't advance batch") - } - endMsgCount, err := inboxTracker.GetBatchMessageCount(endGs.Batch - 1) - if err != nil { - return nil, errors.Wrap(err, "failed to get challenge end batch metadata") + var endMsgCount arbutil.MessageIndex + if endGs.Batch > 0 { + endMsgCount, err = inboxTracker.GetBatchMessageCount(endGs.Batch - 1) + if err != nil { + return nil, errors.Wrap(err, "failed to get challenge end batch metadata") + } } + endMsgCount += arbutil.MessageIndex(endGs.PosInBatch) return &BlockChallengeBackend{ client: client, @@ -193,7 +189,7 @@ func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, h if err != nil { return GoGlobalState{}, err } - if batchMsgCount >= msgCount { + if batchMsgCount > msgCount { return GoGlobalState{}, errors.New("findBatchFromMessageCount returned bad batch") } } diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 8882af2a2a..0012e7e992 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -322,11 +322,11 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in return fmt.Errorf("block header %v before challenge point unknown", blockNum) } } - globalState, err := m.blockChallengeBackend.FindGlobalStateFromHeader(ctx, blockHeader) + startGlobalState, err := m.blockChallengeBackend.FindGlobalStateFromHeader(ctx, blockHeader) if err != nil { return err } - err = machine.SetGlobalState(globalState) + err = machine.SetGlobalState(startGlobalState) if err != nil { return err } @@ -360,11 +360,11 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in return err } } - batchBytes, err := m.inboxReader.GetSequencerMessageBytes(ctx, globalState.Batch) + batchBytes, err := m.inboxReader.GetSequencerMessageBytes(ctx, startGlobalState.Batch) if err != nil { return err } - err = machine.AddSequencerInboxMessage(globalState.Batch, batchBytes) + err = machine.AddSequencerInboxMessage(startGlobalState.Batch, batchBytes) if err != nil { return err } From 730dbe3aa026434806e73e9e04b0e64872251cb0 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 20:23:48 -0500 Subject: [PATCH 040/110] Fixup validator wallet --- solgen/src/rollup/ValidatorWallet.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solgen/src/rollup/ValidatorWallet.sol b/solgen/src/rollup/ValidatorWallet.sol index fc68eb545e..9ac86de413 100644 --- a/solgen/src/rollup/ValidatorWallet.sol +++ b/solgen/src/rollup/ValidatorWallet.sol @@ -20,14 +20,14 @@ pragma solidity ^0.8.0; import "./IRollupLogic.sol"; import "../challenge/IChallenge.sol"; -import "../libraries/Cloneable.sol"; +import "../libraries/DelegateCallAware.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -contract ValidatorWallet is OwnableUpgradeable, Cloneable { +contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { using Address for address; - function initialize() external initializer { + function initialize() external initializer onlyDelegated { __Ownable_init(); } From 3f1073dd7c16e2ca15559a505f12b852a0e6fe8e Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 21:32:15 -0500 Subject: [PATCH 041/110] Merge block and execution challenge --- arbnode/node.go | 8 +- solgen/src/challenge/BlockChallenge.sol | 138 ++++++++++-------- .../src/challenge/BlockChallengeFactory.sol | 8 +- solgen/src/challenge/ExecutionChallenge.sol | 102 ------------- .../challenge/ExecutionChallengeFactory.sol | 45 ------ .../challenge/IExecutionChallengeFactory.sol | 21 --- solgen/src/mocks/SingleExecutionChallenge.sol | 42 +++--- validator/block_challenge_backend.go | 24 --- validator/challenge_manager.go | 45 ++++-- validator/challenge_test.go | 4 +- validator/execution_challenge_backend.go | 2 +- 11 files changed, 141 insertions(+), 298 deletions(-) delete mode 100644 solgen/src/challenge/ExecutionChallenge.sol delete mode 100644 solgen/src/challenge/ExecutionChallengeFactory.sol delete mode 100644 solgen/src/challenge/IExecutionChallengeFactory.sol diff --git a/arbnode/node.go b/arbnode/node.go index cde20edb7a..17d36284a5 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -138,13 +138,7 @@ func deployChallengeFactory(ctx context.Context, client arbutil.L1Interface, aut return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - execChallengeFactoryAddr, tx, _, err := challengegen.DeployExecutionChallengeFactory(auth, client, ospEntryAddr) - err = andTxSucceeded(ctx, client, txTimeout, tx, err) - if err != nil { - return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) - } - - blockChallengeFactoryAddr, tx, _, err := challengegen.DeployBlockChallengeFactory(auth, client, execChallengeFactoryAddr) + blockChallengeFactoryAddr, tx, _, err := challengegen.DeployBlockChallengeFactory(auth, client, ospEntryAddr) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) diff --git a/solgen/src/challenge/BlockChallenge.sol b/solgen/src/challenge/BlockChallenge.sol index 23dc5252c2..a17becc49a 100644 --- a/solgen/src/challenge/BlockChallenge.sol +++ b/solgen/src/challenge/BlockChallenge.sol @@ -8,29 +8,35 @@ import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; import "./ChallengeCore.sol"; import "./IChallenge.sol"; -import "./IExecutionChallengeFactory.sol"; import "./IBlockChallengeFactory.sol"; -contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultReceiver { +contract BlockChallenge is ChallengeCore, DelegateCallAware { using GlobalStateLib for GlobalState; using MachineLib for Machine; - event ExecutionChallengeBegun(IChallenge indexed challenge, uint256 blockSteps); + enum ChallengeMode { + NONE, + BLOCK, + EXECUTION + } - IExecutionChallengeFactory public executionChallengeFactory; + event ExecutionChallengeBegun(uint256 blockSteps); + event OneStepProofCompleted(); bytes32 public wasmModuleRoot; GlobalState[2] internal startAndEndGlobalStates; - IChallenge public executionChallenge; - uint256 public executionChallengeAtSteps; - ISequencerInbox public sequencerInbox; IBridge public delayedBridge; + IOneStepProofEntry public osp; + + ChallengeMode public mode; + + uint256 maxInboxMessages; // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function initialize( - IExecutionChallengeFactory executionChallengeFactory_, + IOneStepProofEntry osp_, IBlockChallengeFactory.ChallengeContracts memory contractAddresses, bytes32 wasmModuleRoot_, MachineStatus[2] memory startAndEndMachineStatuses_, @@ -43,10 +49,10 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec ) external onlyDelegated { require(address(resultReceiver) == address(0), "ALREADY_INIT"); require(address(contractAddresses.resultReceiver) != address(0), "NO_RESULT_RECEIVER"); - executionChallengeFactory = executionChallengeFactory_; resultReceiver = IChallengeResultReceiver(contractAddresses.resultReceiver); sequencerInbox = ISequencerInbox(contractAddresses.sequencerInbox); delayedBridge = IBridge(contractAddresses.delayedBridge); + osp = osp_; wasmModuleRoot = wasmModuleRoot_; startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; @@ -56,6 +62,7 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec challengerTimeLeft = challengerTimeLeft_; lastMoveTimestamp = block.timestamp; turn = Turn.CHALLENGER; + mode = ChallengeMode.BLOCK; bytes32[] memory segments = new bytes32[](2); segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); @@ -94,8 +101,7 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec "EXEC_DEADLINE" ); - uint256 challengeLength; - (executionChallengeAtSteps, challengeLength) = extractChallengeSegment( + (uint256 executionChallengeAtSteps, uint256 challengeLength) = extractChallengeSegment( oldSegmentsStart, oldSegmentsLength, oldSegments, @@ -103,21 +109,6 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec ); require(challengeLength == 1, "TOO_LONG"); - address newAsserter = asserter; - address newChallenger = challenger; - uint256 newAsserterTimeLeft = asserterTimeLeft; - uint256 newChallengerTimeLeft = challengerTimeLeft; - - if (turn == Turn.CHALLENGER) { - (newAsserter, newChallenger) = (newChallenger, newAsserter); - (newAsserterTimeLeft, newChallengerTimeLeft) = ( - newChallengerTimeLeft, - newAsserterTimeLeft - ); - } else if (turn != Turn.ASSERTER) { - revert(NO_TURN); - } - require( oldSegments[challengePosition] == ChallengeLib.blockStateHash( @@ -164,25 +155,68 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec if (machineStatuses[1] == MachineStatus.ERRORED || startAndEndGlobalStates[1].getPositionInMessage() > 0) { maxInboxMessagesRead++; } - ExecutionContext memory execCtx = ExecutionContext({ - maxInboxMessagesRead: maxInboxMessagesRead, - sequencerInbox: sequencerInbox, - delayedBridge: delayedBridge - }); - - executionChallenge = executionChallengeFactory.createChallenge( - this, - execCtx, - startAndEndHashes, - numSteps, - newAsserter, - newChallenger, - newAsserterTimeLeft, - newChallengerTimeLeft + + + if (turn == Turn.CHALLENGER) { + (asserter, challenger) = (challenger, asserter); + (asserterTimeLeft, challengerTimeLeft) = (challengerTimeLeft, asserterTimeLeft); + } else if (turn != Turn.ASSERTER) { + revert(NO_TURN); + } + + require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); + maxInboxMessages = maxInboxMessages; + bytes32[] memory segments = new bytes32[](2); + segments[0] = startAndEndHashes[0]; + segments[1] = startAndEndHashes[1]; + challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps, segments); + lastMoveTimestamp = block.timestamp; + turn = Turn.CHALLENGER; + mode = ChallengeMode.EXECUTION; + + emit InitiatedChallenge(); + emit Bisected( + challengeStateHash, + 0, + numSteps, + segments + ); + + emit ExecutionChallengeBegun(executionChallengeAtSteps); + } + + function oneStepProveExecution( + uint256 oldSegmentsStart, + uint256 oldSegmentsLength, + bytes32[] calldata oldSegments, + uint256 challengePosition, + bytes calldata proof + ) external takeTurn { + (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment( + oldSegmentsStart, + oldSegmentsLength, + oldSegments, + challengePosition + ); + require(challengeLength == 1, "TOO_LONG"); + + bytes32 afterHash = osp.proveOneStep( + ExecutionContext({ + maxInboxMessagesRead: maxInboxMessages, + sequencerInbox: sequencerInbox, + delayedBridge: delayedBridge + }), + challengeStart, + oldSegments[challengePosition], + proof + ); + require( + afterHash != oldSegments[challengePosition + 1], + "SAME_OSP_END" ); - turn = Turn.NO_CHALLENGE; - emit ExecutionChallengeBegun(executionChallenge, executionChallengeAtSteps); + emit OneStepProofCompleted(); + _currentWin(); } function getStartMachineHash(bytes32 globalStateHash) @@ -246,24 +280,6 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware, IChallengeResultRec function clearChallenge() external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); turn = Turn.NO_CHALLENGE; - if (address(executionChallenge) != address(0)) { - executionChallenge.clearChallenge(); - } - } - - function completeChallenge(address winner, address loser) - external - override - { - require(msg.sender == address(executionChallenge), "NOT_EXEC_CHAL"); - // since this is being called by the execution challenge, - // and since we transition to NO_CHALLENGE when we create - // an execution challenge, that must mean the state is - // already NO_CHALLENGE. So we dont technically need to set that here. - // However to guard against a possible future missed refactoring - // it's probably safest to set it here anyway - if(turn != Turn.NO_CHALLENGE) turn = Turn.NO_CHALLENGE; - resultReceiver.completeChallenge(winner, loser); } function _currentWin() private { diff --git a/solgen/src/challenge/BlockChallengeFactory.sol b/solgen/src/challenge/BlockChallengeFactory.sol index 3dc153e5a7..b8e5ff6d54 100644 --- a/solgen/src/challenge/BlockChallengeFactory.sol +++ b/solgen/src/challenge/BlockChallengeFactory.sol @@ -7,11 +7,11 @@ import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; contract BlockChallengeFactory is IBlockChallengeFactory { - IExecutionChallengeFactory public executionChallengeFactory; UpgradeableBeacon public beacon; + IOneStepProofEntry public osp; - constructor(IExecutionChallengeFactory executionChallengeFactory_) { - executionChallengeFactory = executionChallengeFactory_; + constructor(IOneStepProofEntry osp_) { + osp = osp_; address challengeTemplate = address(new BlockChallenge()); beacon = new UpgradeableBeacon(challengeTemplate); beacon.transferOwnership(msg.sender); @@ -30,7 +30,7 @@ contract BlockChallengeFactory is IBlockChallengeFactory { ) external override returns (IChallenge) { address clone = address(new BeaconProxy(address(beacon), "")); BlockChallenge(clone).initialize( - executionChallengeFactory, + osp, contractAddresses, wasmModuleRoot_, startAndEndMachineStatuses_, diff --git a/solgen/src/challenge/ExecutionChallenge.sol b/solgen/src/challenge/ExecutionChallenge.sol deleted file mode 100644 index 3195adef77..0000000000 --- a/solgen/src/challenge/ExecutionChallenge.sol +++ /dev/null @@ -1,102 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "../libraries/DelegateCallAware.sol"; -import "../osp/IOneStepProofEntry.sol"; -import "./IChallengeResultReceiver.sol"; -import "./ChallengeLib.sol"; -import "./ChallengeCore.sol"; -import "./IChallenge.sol"; - -contract ExecutionChallenge is ChallengeCore, DelegateCallAware { - event OneStepProofCompleted(); - - IOneStepProofEntry public osp; - ExecutionContext public execCtx; - - function initialize( - IOneStepProofEntry osp_, - IChallengeResultReceiver resultReceiver_, - ExecutionContext memory execCtx_, - bytes32[2] memory startAndEndHashes, - uint256 challenge_length, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) public onlyDelegated { - require(address(resultReceiver) == address(0), "ALREADY_INIT"); - require(address(resultReceiver_) != address(0), "NO_RESULT_RECEIVER"); - require(challenge_length <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); - osp = osp_; - resultReceiver = resultReceiver_; - execCtx = execCtx_; - bytes32[] memory segments = new bytes32[](2); - segments[0] = startAndEndHashes[0]; - segments[1] = startAndEndHashes[1]; - challengeStateHash = ChallengeLib.hashChallengeState(0, challenge_length, segments); - asserter = asserter_; - challenger = challenger_; - asserterTimeLeft = asserterTimeLeft_; - challengerTimeLeft = challengerTimeLeft_; - lastMoveTimestamp = block.timestamp; - turn = Turn.CHALLENGER; - - emit InitiatedChallenge(); - emit Bisected( - challengeStateHash, - 0, - challenge_length, - segments - ); - } - - function oneStepProveExecution( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition, - bytes calldata proof - ) external takeTurn { - (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment( - oldSegmentsStart, - oldSegmentsLength, - oldSegments, - challengePosition - ); - require(challengeLength == 1, "TOO_LONG"); - - bytes32 afterHash = osp.proveOneStep( - execCtx, - challengeStart, - oldSegments[challengePosition], - proof - ); - require( - afterHash != oldSegments[challengePosition + 1], - "SAME_OSP_END" - ); - - emit OneStepProofCompleted(); - _currentWin(); - } - - function clearChallenge() external override { - require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); - turn = Turn.NO_CHALLENGE; - } - - function _currentWin() private { - // As a safety measure, challenges can only be resolved by timeouts during mainnet beta. - // As state is 0, no move is possible. The other party will lose via timeout - challengeStateHash = bytes32(0); - - // if (turn == Turn.ASSERTER) { - // _asserterWin(); - // } else if (turn == Turn.CHALLENGER) { - // _challengerWin(); - // } else { - // revert(NO_TURN); - // } - } -} diff --git a/solgen/src/challenge/ExecutionChallengeFactory.sol b/solgen/src/challenge/ExecutionChallengeFactory.sol deleted file mode 100644 index e07786d75d..0000000000 --- a/solgen/src/challenge/ExecutionChallengeFactory.sol +++ /dev/null @@ -1,45 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "./ExecutionChallenge.sol"; -import "./IExecutionChallengeFactory.sol"; -import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -contract ExecutionChallengeFactory is IExecutionChallengeFactory { - IOneStepProofEntry public osp; - UpgradeableBeacon public beacon; - - constructor(IOneStepProofEntry osp_) { - osp = osp_; - address challengeTemplate = address(new ExecutionChallenge()); - beacon = new UpgradeableBeacon(challengeTemplate); - beacon.transferOwnership(msg.sender); - } - - function createChallenge( - IChallengeResultReceiver resultReceiver_, - ExecutionContext memory execCtx_, - bytes32[2] memory startAndEndHashes, - uint256 challenge_length_, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) external override returns (IChallenge) { - address clone = address(new BeaconProxy(address(beacon), "")); - ExecutionChallenge(clone).initialize( - osp, - resultReceiver_, - execCtx_, - startAndEndHashes, - challenge_length_, - asserter_, - challenger_, - asserterTimeLeft_, - challengerTimeLeft_ - ); - emit ChallengeCreated(IChallenge(clone)); - return IChallenge(clone); - } -} diff --git a/solgen/src/challenge/IExecutionChallengeFactory.sol b/solgen/src/challenge/IExecutionChallengeFactory.sol deleted file mode 100644 index ecf4962d13..0000000000 --- a/solgen/src/challenge/IExecutionChallengeFactory.sol +++ /dev/null @@ -1,21 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "../osp/IOneStepProofEntry.sol"; -import "./IChallenge.sol"; -import "./IChallengeResultReceiver.sol"; - -interface IExecutionChallengeFactory { - event ChallengeCreated(IChallenge challenge); - - function createChallenge( - IChallengeResultReceiver resultReceiver_, - ExecutionContext memory execCtx_, - bytes32[2] memory startAndEndHashes, - uint256 challenge_length_, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) external returns (IChallenge); -} diff --git a/solgen/src/mocks/SingleExecutionChallenge.sol b/solgen/src/mocks/SingleExecutionChallenge.sol index d4a67f9770..2743d20fd1 100644 --- a/solgen/src/mocks/SingleExecutionChallenge.sol +++ b/solgen/src/mocks/SingleExecutionChallenge.sol @@ -1,33 +1,39 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../challenge/ExecutionChallenge.sol"; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "../challenge/BlockChallenge.sol"; -contract SingleExecutionChallenge is ERC1967Proxy { +contract SingleExecutionChallenge is BlockChallenge { constructor( IOneStepProofEntry osp_, IChallengeResultReceiver resultReceiver_, - ExecutionContext memory execCtx_, + uint256 maxInboxMessagesRead_, bytes32[2] memory startAndEndHashes, uint256 numSteps_, address asserter_, address challenger_, uint256 asserterTimeLeft_, uint256 challengerTimeLeft_ - ) ERC1967Proxy( - address(new ExecutionChallenge()), - abi.encodeWithSelector( - ExecutionChallenge.initialize.selector, - osp_, - resultReceiver_, - execCtx_, - startAndEndHashes, + ) { + osp = osp_; + resultReceiver = resultReceiver_; + maxInboxMessages = maxInboxMessagesRead_; + bytes32[] memory segments = new bytes32[](2); + segments[0] = startAndEndHashes[0]; + segments[1] = startAndEndHashes[1]; + challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); + asserter = asserter_; + challenger = challenger_; + asserterTimeLeft = asserterTimeLeft_; + challengerTimeLeft = challengerTimeLeft_; + lastMoveTimestamp = block.timestamp; + turn = Turn.CHALLENGER; + + emit Bisected( + challengeStateHash, + 0, numSteps_, - asserter_, - challenger_, - asserterTimeLeft_, - challengerTimeLeft_ - ) - ) {} + segments + ); + } } diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 0d1dc97cf1..1be8e024f7 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -292,27 +292,3 @@ func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client b big.NewInt(int64(numsteps)), ) } - -func inExecChallengeError(err error) (bool, common.Address, int64, error) { - return false, common.Address{}, 0, err -} - -func (b *BlockChallengeBackend) IsInExecutionChallenge(ctx context.Context, latestConfirmedBlock *big.Int) (bool, common.Address, int64, error) { - callOpts := &bind.CallOpts{ - Context: ctx, - BlockNumber: latestConfirmedBlock, - } - addr, err := b.blockChallengeCon.ExecutionChallenge(callOpts) - if err != nil { - return inExecChallengeError(err) - } - if addr == (common.Address{}) { - return inExecChallengeError(nil) - } - - blockOffset, err := b.blockChallengeCon.ExecutionChallengeAtSteps(callOpts) - if err != nil { - return inExecChallengeError(err) - } - return true, addr, b.startBlock + blockOffset.Int64(), nil -} diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 0012e7e992..ed8bf13603 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -25,7 +25,10 @@ import ( const maxBisectionDegree uint64 = 40 +const challengeModeExecution = 2 + var challengeBisectedID common.Hash +var executionChallengeBegunID common.Hash func init() { parsedChallengeCoreABI, err := abi.JSON(strings.NewReader(challengegen.ChallengeCoreABI)) @@ -33,6 +36,7 @@ func init() { panic(err) } challengeBisectedID = parsedChallengeCoreABI.Events["Bisected"].ID + executionChallengeBegunID = parsedChallengeCoreABI.Events["ExecutionChallengeBegun"].ID } type ChallengeBackend interface { @@ -42,7 +46,7 @@ type ChallengeBackend interface { type ChallengeManager struct { // fields used in both block and execution challenge - con *challengegen.ChallengeCore + con *challengegen.BlockChallenge challengeAddr common.Address rootChallengeAddr common.Address client bind.ContractBackend @@ -69,7 +73,7 @@ type ChallengeManager struct { } func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, fromAddr common.Address, blockChallengeAddr common.Address, l2blockChain *core.BlockChain, inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, startL1Block uint64, targetNumMachines int, confirmationBlocks int64) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewChallengeCore(blockChallengeAddr, l1client) + challengeCoreCon, err := challengegen.NewBlockChallenge(blockChallengeAddr, l1client) if err != nil { return nil, err } @@ -101,7 +105,7 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut // for testing only - skips block challenges func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, execChallengeAddr common.Address, initialMachine MachineInterface, startL1Block uint64, targetNumMachines int) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewChallengeCore(execChallengeAddr, l1client) + challengeCoreCon, err := challengegen.NewBlockChallenge(execChallengeAddr, l1client) if err != nil { return nil, err } @@ -169,8 +173,8 @@ func (m *ChallengeManager) resolveStateHash(ctx context.Context, stateHash commo } // Multiple logs are in theory fine, as they should all reveal the same preimage. // We'll use the most recent log to be safe. - log := logs[len(logs)-1] - parsedLog, err := m.con.ParseBisected(log) + evmLog := logs[len(logs)-1] + parsedLog, err := m.con.ParseBisected(evmLog) if err != nil { return ChallengeState{}, err } @@ -383,14 +387,33 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { if err != nil { return err } - inExec, addr, blockNum, err := m.blockChallengeBackend.IsInExecutionChallenge(ctx, latestConfirmedBlock) - if err != nil || !inExec { - return err + callOpts := &bind.CallOpts{ + Context: ctx, + BlockNumber: latestConfirmedBlock, } - con, err := challengegen.NewChallengeCore(addr, m.client) + challengeMode, err := m.con.Mode(callOpts) + if err != nil || challengeMode != challengeModeExecution { + return errors.WithStack(err) + } + logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: m.startL1Block, + Addresses: []common.Address{m.challengeAddr}, + Topics: [][]common.Hash{{executionChallengeBegunID}}, + }) if err != nil { - return err + return errors.WithStack(err) + } + if len(logs) == 0 { + return errors.New("expected ExecutionChallengeBegun event") + } + if len(logs) > 1 { + return errors.New("expected only one ExecutionChallengeBegun event") + } + ev, err := m.con.ParseExecutionChallengeBegun(logs[0]) + if err != nil { + return errors.WithStack(err) } + blockNum := m.blockChallengeBackend.startBlock + ev.BlockSteps.Int64() err = m.createInitialMachine(ctx, blockNum) if err != nil { return err @@ -399,9 +422,7 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { if err != nil { return err } - m.con = con m.executionChallengeBackend = execBackend - m.challengeAddr = addr return nil } diff --git a/validator/challenge_test.go b/validator/challenge_test.go index 1e9603b431..070f3d8b39 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -64,9 +64,7 @@ func CreateChallenge( client, ospEntry, resultReceiverAddr, - mocksgen.ExecutionContext{ - MaxInboxMessagesRead: new(big.Int).SetUint64(^uint64(0)), - }, + new(big.Int).SetUint64(^uint64(0)), [2][32]byte{startHashBytes, endHashBytes}, big.NewInt(int64(endMachineSteps)), asserter, diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index cbd35b9393..33805407b2 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -84,7 +84,7 @@ func (b *ExecutionChallengeBackend) GetHashAtStep(ctx context.Context, position } func (b *ExecutionChallengeBackend) IssueOneStepProof(ctx context.Context, client bind.ContractBackend, auth *bind.TransactOpts, challenge common.Address, oldState *ChallengeState, startSegment int) (*types.Transaction, error) { - con, err := challengegen.NewExecutionChallenge(challenge, client) + con, err := challengegen.NewBlockChallenge(challenge, client) if err != nil { return nil, err } From 29e5bf5f9f1ae5c2e66e39fb23fc58a99fb03788 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 21:50:03 -0500 Subject: [PATCH 042/110] Rename blockchallenge to challenge --- arbnode/node.go | 4 ++-- .../{BlockChallenge.sol => Challenge.sol} | 6 +++--- ...hallengeFactory.sol => ChallengeFactory.sol} | 10 +++++----- ...allengeFactory.sol => IChallengeFactory.sol} | 2 +- solgen/src/mocks/SingleExecutionChallenge.sol | 4 ++-- solgen/src/rollup/RollupAdminLogic.sol | 2 +- solgen/src/rollup/RollupCore.sol | 4 ++-- solgen/src/rollup/RollupCreator.sol | 6 +++--- solgen/src/rollup/RollupLib.sol | 4 ++-- solgen/src/rollup/RollupUserLogic.sol | 2 +- solgen/src/state/Machine.sol | 2 +- system_tests/full_challenge_test.go | 17 ++++------------- validator/block_challenge_backend.go | 8 ++++---- validator/challenge_manager.go | 13 +++++-------- validator/execution_challenge_backend.go | 2 +- validator/staker.go | 2 +- 16 files changed, 38 insertions(+), 50 deletions(-) rename solgen/src/challenge/{BlockChallenge.sol => Challenge.sol} (98%) rename solgen/src/challenge/{BlockChallengeFactory.sol => ChallengeFactory.sol} (84%) rename solgen/src/challenge/{IBlockChallengeFactory.sol => IChallengeFactory.sol} (95%) diff --git a/arbnode/node.go b/arbnode/node.go index 17d36284a5..dc571cab3a 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -138,13 +138,13 @@ func deployChallengeFactory(ctx context.Context, client arbutil.L1Interface, aut return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - blockChallengeFactoryAddr, tx, _, err := challengegen.DeployBlockChallengeFactory(auth, client, ospEntryAddr) + challengeFactoryAddr, tx, _, err := challengegen.DeployChallengeFactory(auth, client, ospEntryAddr) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - return blockChallengeFactoryAddr, nil + return challengeFactoryAddr, nil } func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (*rollupgen.RollupCreator, common.Address, error) { diff --git a/solgen/src/challenge/BlockChallenge.sol b/solgen/src/challenge/Challenge.sol similarity index 98% rename from solgen/src/challenge/BlockChallenge.sol rename to solgen/src/challenge/Challenge.sol index a17becc49a..e443685cbd 100644 --- a/solgen/src/challenge/BlockChallenge.sol +++ b/solgen/src/challenge/Challenge.sol @@ -8,9 +8,9 @@ import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; import "./ChallengeCore.sol"; import "./IChallenge.sol"; -import "./IBlockChallengeFactory.sol"; +import "./IChallengeFactory.sol"; -contract BlockChallenge is ChallengeCore, DelegateCallAware { +contract Challenge is ChallengeCore, DelegateCallAware { using GlobalStateLib for GlobalState; using MachineLib for Machine; @@ -37,7 +37,7 @@ contract BlockChallenge is ChallengeCore, DelegateCallAware { // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function initialize( IOneStepProofEntry osp_, - IBlockChallengeFactory.ChallengeContracts memory contractAddresses, + IChallengeFactory.ChallengeContracts memory contractAddresses, bytes32 wasmModuleRoot_, MachineStatus[2] memory startAndEndMachineStatuses_, GlobalState[2] memory startAndEndGlobalStates_, diff --git a/solgen/src/challenge/BlockChallengeFactory.sol b/solgen/src/challenge/ChallengeFactory.sol similarity index 84% rename from solgen/src/challenge/BlockChallengeFactory.sol rename to solgen/src/challenge/ChallengeFactory.sol index b8e5ff6d54..dc80d112cd 100644 --- a/solgen/src/challenge/BlockChallengeFactory.sol +++ b/solgen/src/challenge/ChallengeFactory.sol @@ -1,18 +1,18 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "./BlockChallenge.sol"; -import "./IBlockChallengeFactory.sol"; +import "./Challenge.sol"; +import "./IChallengeFactory.sol"; import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; -contract BlockChallengeFactory is IBlockChallengeFactory { +contract ChallengeFactory is IChallengeFactory { UpgradeableBeacon public beacon; IOneStepProofEntry public osp; constructor(IOneStepProofEntry osp_) { osp = osp_; - address challengeTemplate = address(new BlockChallenge()); + address challengeTemplate = address(new Challenge()); beacon = new UpgradeableBeacon(challengeTemplate); beacon.transferOwnership(msg.sender); } @@ -29,7 +29,7 @@ contract BlockChallengeFactory is IBlockChallengeFactory { uint256 challengerTimeLeft_ ) external override returns (IChallenge) { address clone = address(new BeaconProxy(address(beacon), "")); - BlockChallenge(clone).initialize( + Challenge(clone).initialize( osp, contractAddresses, wasmModuleRoot_, diff --git a/solgen/src/challenge/IBlockChallengeFactory.sol b/solgen/src/challenge/IChallengeFactory.sol similarity index 95% rename from solgen/src/challenge/IBlockChallengeFactory.sol rename to solgen/src/challenge/IChallengeFactory.sol index f18689e78f..d1f3a94a27 100644 --- a/solgen/src/challenge/IBlockChallengeFactory.sol +++ b/solgen/src/challenge/IChallengeFactory.sol @@ -5,7 +5,7 @@ import "../osp/IOneStepProofEntry.sol"; import "./IChallenge.sol"; import "./IChallengeResultReceiver.sol"; -interface IBlockChallengeFactory { +interface IChallengeFactory { event ChallengeCreated(IChallenge challenge); struct ChallengeContracts { diff --git a/solgen/src/mocks/SingleExecutionChallenge.sol b/solgen/src/mocks/SingleExecutionChallenge.sol index 2743d20fd1..5af9d4a042 100644 --- a/solgen/src/mocks/SingleExecutionChallenge.sol +++ b/solgen/src/mocks/SingleExecutionChallenge.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../challenge/BlockChallenge.sol"; +import "../challenge/Challenge.sol"; -contract SingleExecutionChallenge is BlockChallenge { +contract SingleExecutionChallenge is Challenge { constructor( IOneStepProofEntry osp_, IChallengeResultReceiver resultReceiver_, diff --git a/solgen/src/rollup/RollupAdminLogic.sol b/solgen/src/rollup/RollupAdminLogic.sol index 263656d984..26071d796e 100644 --- a/solgen/src/rollup/RollupAdminLogic.sol +++ b/solgen/src/rollup/RollupAdminLogic.sol @@ -25,7 +25,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade rollupEventBridge.rollupInitialized(config.owner, config.chainId); sequencerBridge.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0))); - challengeFactory = connectedContracts.blockChallengeFactory; + challengeFactory = connectedContracts.challengeFactory; Node memory node = createInitialNode(); initializeCore(node); diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 4e6b282367..183c21fba6 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -26,7 +26,7 @@ import "./RollupLib.sol"; import "./RollupEventBridge.sol"; import "./IRollupCore.sol"; -import "../challenge/IBlockChallengeFactory.sol"; +import "../challenge/IChallengeFactory.sol"; import "../bridge/ISequencerInbox.sol"; import "../bridge/IBridge.sol"; @@ -47,7 +47,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { ISequencerInbox public sequencerBridge; IOutbox public outbox; RollupEventBridge public rollupEventBridge; - IBlockChallengeFactory public challengeFactory; + IChallengeFactory public challengeFactory; // when a staker loses a challenge, half of their funds get escrowed in this address address public loserStakeEscrow; address public stakeToken; diff --git a/solgen/src/rollup/RollupCreator.sol b/solgen/src/rollup/RollupCreator.sol index a09f47f28b..b6b8894175 100644 --- a/solgen/src/rollup/RollupCreator.sol +++ b/solgen/src/rollup/RollupCreator.sol @@ -41,7 +41,7 @@ contract RollupCreator is Ownable { event TemplatesUpdated(); BridgeCreator public bridgeCreator; - IBlockChallengeFactory public challengeFactory; + IChallengeFactory public challengeFactory; IRollupAdmin public rollupAdminLogic; IRollupUser public rollupUserLogic; @@ -49,7 +49,7 @@ contract RollupCreator is Ownable { function setTemplates( BridgeCreator _bridgeCreator, - IBlockChallengeFactory _challengeFactory, + IChallengeFactory _challengeFactory, IRollupAdmin _rollupAdminLogic, IRollupUser _rollupUserLogic ) external onlyOwner { @@ -96,7 +96,7 @@ contract RollupCreator is Ownable { sequencerInbox: frame.sequencerInbox, outbox: frame.outbox, rollupEventBridge: frame.rollupEventBridge, - blockChallengeFactory: challengeFactory, + challengeFactory: challengeFactory, rollupAdminLogic: rollupAdminLogic, rollupUserLogic: rollupUserLogic }) diff --git a/solgen/src/rollup/RollupLib.sol b/solgen/src/rollup/RollupLib.sol index cbeccf92fa..55971abe17 100644 --- a/solgen/src/rollup/RollupLib.sol +++ b/solgen/src/rollup/RollupLib.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.0; -import "../challenge/IBlockChallengeFactory.sol"; +import "../challenge/IChallengeFactory.sol"; import "../challenge/ChallengeLib.sol"; import "../state/GlobalState.sol"; import "../bridge/ISequencerInbox.sol"; @@ -45,7 +45,7 @@ struct ContractDependencies { ISequencerInbox sequencerInbox; IOutbox outbox; RollupEventBridge rollupEventBridge; - IBlockChallengeFactory blockChallengeFactory; + IChallengeFactory challengeFactory; IRollupAdmin rollupAdminLogic; IRollupUser rollupUserLogic; diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 476f7a1457..a0a6dc16da 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -366,7 +366,7 @@ abstract contract AbsRollupUserLogic is ) internal returns (IChallenge) { return challengeFactory.createChallenge( - IBlockChallengeFactory.ChallengeContracts({ + IChallengeFactory.ChallengeContracts({ resultReceiver: this, sequencerInbox: sequencerBridge, delayedBridge: delayedBridge diff --git a/solgen/src/state/Machine.sol b/solgen/src/state/Machine.sol index 9526234a70..fc60b0df2d 100644 --- a/solgen/src/state/Machine.sol +++ b/solgen/src/state/Machine.sol @@ -32,7 +32,7 @@ library MachineLib { using ValueStackLib for ValueStack; function hash(Machine memory mach) internal pure returns (bytes32) { - // Warning: the non-running hashes are replicated in BlockChallenge + // Warning: the non-running hashes are replicated in Challenge if (mach.status == MachineStatus.RUNNING) { return keccak256(abi.encodePacked( "Machine running:", diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 07f479c43d..e07c14829f 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -81,7 +81,7 @@ func CreateChallenge( t.Fatal(err) } - executionChallengeFactoryAddr, tx, _, err := challengegen.DeployExecutionChallengeFactory(auth, client, ospEntry) + _, tx, challengeFactory, err := challengegen.DeployChallengeFactory(auth, client, ospEntry) if err != nil { t.Fatal(err) } @@ -90,18 +90,9 @@ func CreateChallenge( t.Fatal(err) } - _, tx, blockChallengeFactory, err := challengegen.DeployBlockChallengeFactory(auth, client, executionChallengeFactoryAddr) - if err != nil { - t.Fatal(err) - } - _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) - if err != nil { - t.Fatal(err) - } - - tx, err = blockChallengeFactory.CreateChallenge( + tx, err = challengeFactory.CreateChallenge( auth, - challengegen.IBlockChallengeFactoryChallengeContracts{ + challengegen.IChallengeFactoryChallengeContracts{ ResultReceiver: resultReceiverAddr, SequencerInbox: sequencerInbox, DelayedBridge: delayedBridge, @@ -126,7 +117,7 @@ func CreateChallenge( t.Fatal(err) } - challengeCreatedEvent, err := blockChallengeFactory.ParseChallengeCreated(*receipt.Logs[len(receipt.Logs)-1]) + challengeCreatedEvent, err := challengeFactory.ParseChallengeCreated(*receipt.Logs[len(receipt.Logs)-1]) if err != nil { t.Fatal(err) } diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 1be8e024f7..dd91debeb2 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -62,7 +62,7 @@ func (s GoGlobalState) AsSolidityStruct() challengegen.GlobalState { } type BlockChallengeBackend struct { - blockChallengeCon *challengegen.BlockChallenge + challengeCon *challengegen.Challenge client bind.ContractBackend bc *core.BlockChain startBlock int64 @@ -80,7 +80,7 @@ var _ ChallengeBackend = (*BlockChallengeBackend)(nil) func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNumber uint64) (*BlockChallengeBackend, error) { callOpts := &bind.CallOpts{Context: ctx} - challengeCon, err := challengegen.NewBlockChallenge(challengeAddr, client) + challengeCon, err := challengegen.NewChallenge(challengeAddr, client) if err != nil { return nil, err } @@ -128,7 +128,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return &BlockChallengeBackend{ client: client, - blockChallengeCon: challengeCon, + challengeCon: challengeCon, bc: bc, startBlock: startBlockNum, startGs: startGs, @@ -262,7 +262,7 @@ func (b *BlockChallengeBackend) GetHashAtStep(ctx context.Context, position uint } func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client bind.ContractBackend, auth *bind.TransactOpts, challenge common.Address, oldState *ChallengeState, startSegment int, numsteps uint64) (*types.Transaction, error) { - con, err := challengegen.NewBlockChallenge(challenge, client) + con, err := challengegen.NewChallenge(challenge, client) if err != nil { return nil, err } diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index ed8bf13603..1468df8210 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -46,9 +46,8 @@ type ChallengeBackend interface { type ChallengeManager struct { // fields used in both block and execution challenge - con *challengegen.BlockChallenge + con *challengegen.Challenge challengeAddr common.Address - rootChallengeAddr common.Address client bind.ContractBackend auth *bind.TransactOpts actingAs common.Address @@ -73,7 +72,7 @@ type ChallengeManager struct { } func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, fromAddr common.Address, blockChallengeAddr common.Address, l2blockChain *core.BlockChain, inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, startL1Block uint64, targetNumMachines int, confirmationBlocks int64) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewBlockChallenge(blockChallengeAddr, l1client) + challengeCoreCon, err := challengegen.NewChallenge(blockChallengeAddr, l1client) if err != nil { return nil, err } @@ -88,7 +87,6 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut return &ChallengeManager{ con: challengeCoreCon, challengeAddr: blockChallengeAddr, - rootChallengeAddr: blockChallengeAddr, client: l1client, auth: auth, actingAs: fromAddr, @@ -105,7 +103,7 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut // for testing only - skips block challenges func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, execChallengeAddr common.Address, initialMachine MachineInterface, startL1Block uint64, targetNumMachines int) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewBlockChallenge(execChallengeAddr, l1client) + challengeCoreCon, err := challengegen.NewChallenge(execChallengeAddr, l1client) if err != nil { return nil, err } @@ -116,7 +114,6 @@ func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBac return &ChallengeManager{ con: challengeCoreCon, challengeAddr: execChallengeAddr, - rootChallengeAddr: execChallengeAddr, client: l1client, auth: auth, actingAs: auth.From, @@ -154,8 +151,8 @@ func (m *ChallengeManager) latestConfirmedBlock(ctx context.Context) (*big.Int, return block, nil } -func (m *ChallengeManager) RootChallengeAddress() common.Address { - return m.rootChallengeAddr +func (m *ChallengeManager) ChallengeAddress() common.Address { + return m.challengeAddr } // Given the challenge's state hash, resolve the full challenge state via the Bisected event. diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index 33805407b2..fd12d1f068 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -84,7 +84,7 @@ func (b *ExecutionChallengeBackend) GetHashAtStep(ctx context.Context, position } func (b *ExecutionChallengeBackend) IssueOneStepProof(ctx context.Context, client bind.ContractBackend, auth *bind.TransactOpts, challenge common.Address, oldState *ChallengeState, startSegment int) (*types.Transaction, error) { - con, err := challengegen.NewBlockChallenge(challenge, client) + con, err := challengegen.NewChallenge(challenge, client) if err != nil { return nil, err } diff --git a/validator/staker.go b/validator/staker.go index ef221e06d0..528a038a38 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -364,7 +364,7 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { return nil } - if s.activeChallenge == nil || s.activeChallenge.RootChallengeAddress() != *info.CurrentChallenge { + if s.activeChallenge == nil || s.activeChallenge.ChallengeAddress() != *info.CurrentChallenge { log.Warn("entered challenge", "challenge", info.CurrentChallenge) latestConfirmedCreated, err := s.rollup.LatestConfirmedCreationBlock(ctx) From cba695dba2a281ebcbf5e3db56a643259a855c8f Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 22:21:44 -0500 Subject: [PATCH 043/110] Move stuff into challenge interface --- solgen/src/challenge/Challenge.sol | 9 --------- solgen/src/challenge/ChallengeCore.sol | 19 +------------------ solgen/src/challenge/IChallenge.sol | 26 ++++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/solgen/src/challenge/Challenge.sol b/solgen/src/challenge/Challenge.sol index e443685cbd..53a9cb028a 100644 --- a/solgen/src/challenge/Challenge.sol +++ b/solgen/src/challenge/Challenge.sol @@ -14,15 +14,6 @@ contract Challenge is ChallengeCore, DelegateCallAware { using GlobalStateLib for GlobalState; using MachineLib for Machine; - enum ChallengeMode { - NONE, - BLOCK, - EXECUTION - } - - event ExecutionChallengeBegun(uint256 blockSteps); - event OneStepProofCompleted(); - bytes32 public wasmModuleRoot; GlobalState[2] internal startAndEndGlobalStates; diff --git a/solgen/src/challenge/ChallengeCore.sol b/solgen/src/challenge/ChallengeCore.sol index 46ebec8b64..60e94d8c56 100644 --- a/solgen/src/challenge/ChallengeCore.sol +++ b/solgen/src/challenge/ChallengeCore.sol @@ -5,24 +5,7 @@ import "./ChallengeLib.sol"; import "./IChallengeResultReceiver.sol"; import "./IChallenge.sol"; -abstract contract ChallengeCore is IChallenge{ - event InitiatedChallenge(); - - enum Turn { - NO_CHALLENGE, - ASSERTER, - CHALLENGER - } - - event Bisected( - bytes32 indexed challengeRoot, - uint256 challengedSegmentStart, - uint256 challengedSegmentLength, - bytes32[] chainHashes - ); - event AsserterTimedOut(); - event ChallengerTimedOut(); - +abstract contract ChallengeCore is IChallenge { address public override asserter; address public override challenger; diff --git a/solgen/src/challenge/IChallenge.sol b/solgen/src/challenge/IChallenge.sol index 98e6bd2143..c91f7faf7b 100644 --- a/solgen/src/challenge/IChallenge.sol +++ b/solgen/src/challenge/IChallenge.sol @@ -2,6 +2,32 @@ pragma solidity ^0.8.0; interface IChallenge { + enum Turn { + NO_CHALLENGE, + ASSERTER, + CHALLENGER + } + + enum ChallengeMode { + NONE, + BLOCK, + EXECUTION + } + + event InitiatedChallenge(); + + event Bisected( + bytes32 indexed challengeRoot, + uint256 challengedSegmentStart, + uint256 challengedSegmentLength, + bytes32[] chainHashes + ); + event AsserterTimedOut(); + event ChallengerTimedOut(); + + event ExecutionChallengeBegun(uint256 blockSteps); + event OneStepProofCompleted(); + function asserter() external view returns (address); function challenger() external view returns (address); function lastMoveTimestamp() external view returns (uint256); From c346237fb8d4f0848ced119918a4e84c82a11c9a Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 22:28:34 -0500 Subject: [PATCH 044/110] Merge challenge and core --- solgen/src/challenge/Challenge.sol | 170 +++++++++++++++++++++++- solgen/src/challenge/ChallengeCore.sol | 171 ------------------------- 2 files changed, 166 insertions(+), 175 deletions(-) delete mode 100644 solgen/src/challenge/ChallengeCore.sol diff --git a/solgen/src/challenge/Challenge.sol b/solgen/src/challenge/Challenge.sol index 53a9cb028a..27b6ce31dd 100644 --- a/solgen/src/challenge/Challenge.sol +++ b/solgen/src/challenge/Challenge.sol @@ -6,17 +6,31 @@ import "../osp/IOneStepProofEntry.sol"; import "../state/GlobalState.sol"; import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; -import "./ChallengeCore.sol"; import "./IChallenge.sol"; import "./IChallengeFactory.sol"; -contract Challenge is ChallengeCore, DelegateCallAware { +contract Challenge is DelegateCallAware, IChallenge { using GlobalStateLib for GlobalState; using MachineLib for Machine; + string constant NO_TURN = "NO_TURN"; + uint256 constant MAX_CHALLENGE_DEGREE = 40; + bytes32 public wasmModuleRoot; GlobalState[2] internal startAndEndGlobalStates; + address public override asserter; + address public override challenger; + + uint256 public asserterTimeLeft; + uint256 public challengerTimeLeft; + uint256 public override lastMoveTimestamp; + + Turn public turn; + bytes32 public challengeStateHash; + + IChallengeResultReceiver public resultReceiver; + ISequencerInbox public sequencerInbox; IBridge public delayedBridge; IOneStepProofEntry public osp; @@ -25,6 +39,25 @@ contract Challenge is ChallengeCore, DelegateCallAware { uint256 maxInboxMessages; + modifier takeTurn() { + require(msg.sender == currentResponder(), "BIS_SENDER"); + require( + block.timestamp - lastMoveTimestamp <= currentResponderTimeLeft(), + "BIS_DEADLINE" + ); + + _; + + if (turn == Turn.CHALLENGER) { + challengerTimeLeft -= block.timestamp - lastMoveTimestamp; + turn = Turn.ASSERTER; + } else { + asserterTimeLeft -= block.timestamp - lastMoveTimestamp; + turn = Turn.CHALLENGER; + } + lastMoveTimestamp = block.timestamp; + } + // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function initialize( IOneStepProofEntry osp_, @@ -77,6 +110,57 @@ contract Challenge is ChallengeCore, DelegateCallAware { return startAndEndGlobalStates[1]; } + /** + * @notice Initiate the next round in the bisection by objecting to execution correctness with a bisection + * of an execution segment with the same length but a different endpoint. This is either the initial move + * or follows another execution objection + */ + function bisectExecution( + uint256 oldSegmentsStart, + uint256 oldSegmentsLength, + bytes32[] calldata oldSegments, + uint256 challengePosition, + bytes32[] calldata newSegments + ) external takeTurn { + ( + uint256 challengeStart, + uint256 challengeLength + ) = extractChallengeSegment( + oldSegmentsStart, + oldSegmentsLength, + oldSegments, + challengePosition + ); + require(challengeLength > 1, "TOO_SHORT"); + { + uint256 expectedDegree = challengeLength; + if (expectedDegree > MAX_CHALLENGE_DEGREE) { + expectedDegree = MAX_CHALLENGE_DEGREE; + } + require(newSegments.length == expectedDegree + 1, "WRONG_DEGREE"); + } + require( + newSegments[newSegments.length - 1] != + oldSegments[challengePosition + 1], + "SAME_END" + ); + + require(oldSegments[challengePosition] == newSegments[0], "DIFF_START"); + + challengeStateHash = ChallengeLib.hashChallengeState( + challengeStart, + challengeLength, + newSegments + ); + + emit Bisected( + challengeStateHash, + challengeStart, + challengeLength, + newSegments + ); + } + function challengeExecution( uint256 oldSegmentsStart, uint256 oldSegmentsLength, @@ -210,6 +294,79 @@ contract Challenge is ChallengeCore, DelegateCallAware { _currentWin(); } + function timeout() external override { + uint256 timeSinceLastMove = block.timestamp - lastMoveTimestamp; + require( + timeSinceLastMove > currentResponderTimeLeft(), + "TIMEOUT_DEADLINE" + ); + + if (turn == Turn.ASSERTER) { + emit AsserterTimedOut(); + _challengerWin(); + } else if (turn == Turn.CHALLENGER) { + emit ChallengerTimedOut(); + _asserterWin(); + } else { + revert(NO_TURN); + } + } + + function clearChallenge() external override { + require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); + turn = Turn.NO_CHALLENGE; + } + + function currentResponder() public view returns (address) { + if (turn == Turn.ASSERTER) { + return asserter; + } else if (turn == Turn.CHALLENGER) { + return challenger; + } else { + revert(NO_TURN); + } + } + + function currentResponderTimeLeft() public override view returns (uint256) { + if (turn == Turn.ASSERTER) { + return asserterTimeLeft; + } else if (turn == Turn.CHALLENGER) { + return challengerTimeLeft; + } else { + revert(NO_TURN); + } + } + + function extractChallengeSegment( + uint256 oldSegmentsStart, + uint256 oldSegmentsLength, + bytes32[] calldata oldSegments, + uint256 challengePosition + ) internal view returns (uint256 segmentStart, uint256 segmentLength) { + require( + challengeStateHash == + ChallengeLib.hashChallengeState( + oldSegmentsStart, + oldSegmentsLength, + oldSegments + ), + "BIS_STATE" + ); + if ( + oldSegments.length < 2 || + challengePosition >= oldSegments.length - 1 + ) { + revert("BAD_CHALLENGE_POS"); + } + uint256 oldChallengeDegree = oldSegments.length - 1; + segmentLength = oldSegmentsLength / oldChallengeDegree; + // Intentionally done before challengeLength is potentially added to for the final segment + segmentStart = oldSegmentsStart + segmentLength * challengePosition; + if (challengePosition == oldSegments.length - 2) { + segmentLength += oldSegmentsLength % oldChallengeDegree; + } + } + function getStartMachineHash(bytes32 globalStateHash) internal view @@ -268,9 +425,14 @@ contract Challenge is ChallengeCore, DelegateCallAware { } } - function clearChallenge() external override { - require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); + function _asserterWin() private { + turn = Turn.NO_CHALLENGE; + resultReceiver.completeChallenge(asserter, challenger); + } + + function _challengerWin() private { turn = Turn.NO_CHALLENGE; + resultReceiver.completeChallenge(challenger, asserter); } function _currentWin() private { diff --git a/solgen/src/challenge/ChallengeCore.sol b/solgen/src/challenge/ChallengeCore.sol deleted file mode 100644 index 60e94d8c56..0000000000 --- a/solgen/src/challenge/ChallengeCore.sol +++ /dev/null @@ -1,171 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "./ChallengeLib.sol"; -import "./IChallengeResultReceiver.sol"; -import "./IChallenge.sol"; - -abstract contract ChallengeCore is IChallenge { - address public override asserter; - address public override challenger; - - uint256 public asserterTimeLeft; - uint256 public challengerTimeLeft; - uint256 public override lastMoveTimestamp; - - Turn public turn; - bytes32 public challengeStateHash; - - string constant NO_TURN = "NO_TURN"; - uint256 constant MAX_CHALLENGE_DEGREE = 40; - - IChallengeResultReceiver public resultReceiver; - - modifier takeTurn() { - require(msg.sender == currentResponder(), "BIS_SENDER"); - require( - block.timestamp - lastMoveTimestamp <= currentResponderTimeLeft(), - "BIS_DEADLINE" - ); - - _; - - if (turn == Turn.CHALLENGER) { - challengerTimeLeft -= block.timestamp - lastMoveTimestamp; - turn = Turn.ASSERTER; - } else { - asserterTimeLeft -= block.timestamp - lastMoveTimestamp; - turn = Turn.CHALLENGER; - } - lastMoveTimestamp = block.timestamp; - } - - function currentResponder() public view returns (address) { - if (turn == Turn.ASSERTER) { - return asserter; - } else if (turn == Turn.CHALLENGER) { - return challenger; - } else { - revert(NO_TURN); - } - } - - function currentResponderTimeLeft() public override view returns (uint256) { - if (turn == Turn.ASSERTER) { - return asserterTimeLeft; - } else if (turn == Turn.CHALLENGER) { - return challengerTimeLeft; - } else { - revert(NO_TURN); - } - } - - function extractChallengeSegment( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition - ) internal view returns (uint256 segmentStart, uint256 segmentLength) { - require( - challengeStateHash == - ChallengeLib.hashChallengeState( - oldSegmentsStart, - oldSegmentsLength, - oldSegments - ), - "BIS_STATE" - ); - if ( - oldSegments.length < 2 || - challengePosition >= oldSegments.length - 1 - ) { - revert("BAD_CHALLENGE_POS"); - } - uint256 oldChallengeDegree = oldSegments.length - 1; - segmentLength = oldSegmentsLength / oldChallengeDegree; - // Intentionally done before challengeLength is potentially added to for the final segment - segmentStart = oldSegmentsStart + segmentLength * challengePosition; - if (challengePosition == oldSegments.length - 2) { - segmentLength += oldSegmentsLength % oldChallengeDegree; - } - } - - /** - * @notice Initiate the next round in the bisection by objecting to execution correctness with a bisection - * of an execution segment with the same length but a different endpoint. This is either the initial move - * or follows another execution objection - */ - function bisectExecution( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition, - bytes32[] calldata newSegments - ) external takeTurn { - ( - uint256 challengeStart, - uint256 challengeLength - ) = extractChallengeSegment( - oldSegmentsStart, - oldSegmentsLength, - oldSegments, - challengePosition - ); - require(challengeLength > 1, "TOO_SHORT"); - { - uint256 expectedDegree = challengeLength; - if (expectedDegree > MAX_CHALLENGE_DEGREE) { - expectedDegree = MAX_CHALLENGE_DEGREE; - } - require(newSegments.length == expectedDegree + 1, "WRONG_DEGREE"); - } - require( - newSegments[newSegments.length - 1] != - oldSegments[challengePosition + 1], - "SAME_END" - ); - - require(oldSegments[challengePosition] == newSegments[0], "DIFF_START"); - - challengeStateHash = ChallengeLib.hashChallengeState( - challengeStart, - challengeLength, - newSegments - ); - - emit Bisected( - challengeStateHash, - challengeStart, - challengeLength, - newSegments - ); - } - - function timeout() external override { - uint256 timeSinceLastMove = block.timestamp - lastMoveTimestamp; - require( - timeSinceLastMove > currentResponderTimeLeft(), - "TIMEOUT_DEADLINE" - ); - - if (turn == Turn.ASSERTER) { - emit AsserterTimedOut(); - _challengerWin(); - } else if (turn == Turn.CHALLENGER) { - emit ChallengerTimedOut(); - _asserterWin(); - } else { - revert(NO_TURN); - } - } - - function _asserterWin() private { - turn = Turn.NO_CHALLENGE; - resultReceiver.completeChallenge(asserter, challenger); - } - - function _challengerWin() private { - turn = Turn.NO_CHALLENGE; - resultReceiver.completeChallenge(challenger, asserter); - } -} From 40ebcbef0af685e26afeca4e8e214f839b970595 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 23:27:48 -0500 Subject: [PATCH 045/110] Move challenge data into struct --- solgen/src/challenge/Challenge.sol | 144 +++++++++--------- solgen/src/challenge/IChallenge.sol | 29 +++- solgen/src/mocks/SingleExecutionChallenge.sol | 17 ++- solgen/src/rollup/ValidatorUtils.sol | 15 +- validator/challenge_manager.go | 19 +-- 5 files changed, 123 insertions(+), 101 deletions(-) diff --git a/solgen/src/challenge/Challenge.sol b/solgen/src/challenge/Challenge.sol index 27b6ce31dd..68fa719b48 100644 --- a/solgen/src/challenge/Challenge.sol +++ b/solgen/src/challenge/Challenge.sol @@ -16,18 +16,7 @@ contract Challenge is DelegateCallAware, IChallenge { string constant NO_TURN = "NO_TURN"; uint256 constant MAX_CHALLENGE_DEGREE = 40; - bytes32 public wasmModuleRoot; - GlobalState[2] internal startAndEndGlobalStates; - - address public override asserter; - address public override challenger; - - uint256 public asserterTimeLeft; - uint256 public challengerTimeLeft; - uint256 public override lastMoveTimestamp; - - Turn public turn; - bytes32 public challengeStateHash; + ChallengeData public challenge; IChallengeResultReceiver public resultReceiver; @@ -35,36 +24,36 @@ contract Challenge is DelegateCallAware, IChallenge { IBridge public delayedBridge; IOneStepProofEntry public osp; - ChallengeMode public mode; - - uint256 maxInboxMessages; + function challengeInfo() external view override returns (ChallengeData memory) { + return challenge; + } modifier takeTurn() { require(msg.sender == currentResponder(), "BIS_SENDER"); require( - block.timestamp - lastMoveTimestamp <= currentResponderTimeLeft(), + block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(), "BIS_DEADLINE" ); _; - if (turn == Turn.CHALLENGER) { - challengerTimeLeft -= block.timestamp - lastMoveTimestamp; - turn = Turn.ASSERTER; + if (challenge.turn == Turn.CHALLENGER) { + challenge.challengerTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; + challenge.turn = Turn.ASSERTER; } else { - asserterTimeLeft -= block.timestamp - lastMoveTimestamp; - turn = Turn.CHALLENGER; + challenge.asserterTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; + challenge.turn = Turn.CHALLENGER; } - lastMoveTimestamp = block.timestamp; + challenge.lastMoveTimestamp = block.timestamp; } // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function initialize( IOneStepProofEntry osp_, - IChallengeFactory.ChallengeContracts memory contractAddresses, + IChallengeFactory.ChallengeContracts calldata contractAddresses, bytes32 wasmModuleRoot_, - MachineStatus[2] memory startAndEndMachineStatuses_, - GlobalState[2] memory startAndEndGlobalStates_, + MachineStatus[2] calldata startAndEndMachineStatuses_, + GlobalState[2] calldata startAndEndGlobalStates_, uint64 numBlocks, address asserter_, address challenger_, @@ -77,21 +66,24 @@ contract Challenge is DelegateCallAware, IChallenge { sequencerInbox = ISequencerInbox(contractAddresses.sequencerInbox); delayedBridge = IBridge(contractAddresses.delayedBridge); osp = osp_; - wasmModuleRoot = wasmModuleRoot_; - startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; - startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; - asserter = asserter_; - challenger = challenger_; - asserterTimeLeft = asserterTimeLeft_; - challengerTimeLeft = challengerTimeLeft_; - lastMoveTimestamp = block.timestamp; - turn = Turn.CHALLENGER; - mode = ChallengeMode.BLOCK; bytes32[] memory segments = new bytes32[](2); segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); segments[1] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[1], startAndEndGlobalStates_[1].hash()); - challengeStateHash = ChallengeLib.hashChallengeState(0, numBlocks, segments); + bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numBlocks, segments); + + challenge.wasmModuleRoot = wasmModuleRoot_; + // No need to set maxInboxMessages until execution challenge + challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; + challenge.startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; + challenge.asserter = asserter_; + challenge.challenger = challenger_; + challenge.asserterTimeLeft = asserterTimeLeft_; + challenge.challengerTimeLeft = challengerTimeLeft_; + challenge.lastMoveTimestamp = block.timestamp; + challenge.turn = Turn.CHALLENGER; + challenge.mode = ChallengeMode.BLOCK; + challenge.challengeStateHash = challengeStateHash; emit InitiatedChallenge(); emit Bisected( @@ -103,11 +95,11 @@ contract Challenge is DelegateCallAware, IChallenge { } function getStartGlobalState() external view returns (GlobalState memory) { - return startAndEndGlobalStates[0]; + return challenge.startAndEndGlobalStates[0]; } function getEndGlobalState() external view returns (GlobalState memory) { - return startAndEndGlobalStates[1]; + return challenge.startAndEndGlobalStates[1]; } /** @@ -147,11 +139,12 @@ contract Challenge is DelegateCallAware, IChallenge { require(oldSegments[challengePosition] == newSegments[0], "DIFF_START"); - challengeStateHash = ChallengeLib.hashChallengeState( + bytes32 challengeStateHash = ChallengeLib.hashChallengeState( challengeStart, challengeLength, newSegments ); + challenge.challengeStateHash = challengeStateHash; emit Bisected( challengeStateHash, @@ -172,7 +165,7 @@ contract Challenge is DelegateCallAware, IChallenge { ) external { require(msg.sender == currentResponder(), "EXEC_SENDER"); require( - block.timestamp - lastMoveTimestamp <= currentResponderTimeLeft(), + block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(), "EXEC_DEADLINE" ); @@ -226,34 +219,41 @@ contract Challenge is DelegateCallAware, IChallenge { globalStateHashes[1] ); - uint256 maxInboxMessagesRead = startAndEndGlobalStates[1].getInboxPosition(); - if (machineStatuses[1] == MachineStatus.ERRORED || startAndEndGlobalStates[1].getPositionInMessage() > 0) { + uint256 maxInboxMessagesRead = challenge.startAndEndGlobalStates[1].getInboxPosition(); + if (machineStatuses[1] == MachineStatus.ERRORED || challenge.startAndEndGlobalStates[1].getPositionInMessage() > 0) { maxInboxMessagesRead++; } - if (turn == Turn.CHALLENGER) { - (asserter, challenger) = (challenger, asserter); - (asserterTimeLeft, challengerTimeLeft) = (challengerTimeLeft, asserterTimeLeft); - } else if (turn != Turn.ASSERTER) { + if (challenge.turn == Turn.CHALLENGER) { + (challenge.asserter, challenge.challenger) = (challenge.challenger, challenge.asserter); + ( + challenge.asserterTimeLeft, + challenge.challengerTimeLeft + ) = ( + challenge.challengerTimeLeft, + challenge.asserterTimeLeft + ); + } else if (challenge.turn != Turn.ASSERTER) { revert(NO_TURN); } require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); - maxInboxMessages = maxInboxMessages; + challenge.maxInboxMessages = challenge.maxInboxMessages; bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; segments[1] = startAndEndHashes[1]; - challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps, segments); - lastMoveTimestamp = block.timestamp; - turn = Turn.CHALLENGER; - mode = ChallengeMode.EXECUTION; + bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps, segments); + challenge.challengeStateHash = challengeStateHash; + challenge.lastMoveTimestamp = block.timestamp; + challenge.turn = Turn.CHALLENGER; + challenge.mode = ChallengeMode.EXECUTION; emit InitiatedChallenge(); emit Bisected( challengeStateHash, 0, - numSteps, + numSteps, segments ); @@ -277,7 +277,7 @@ contract Challenge is DelegateCallAware, IChallenge { bytes32 afterHash = osp.proveOneStep( ExecutionContext({ - maxInboxMessagesRead: maxInboxMessages, + maxInboxMessagesRead: challenge.maxInboxMessages, sequencerInbox: sequencerInbox, delayedBridge: delayedBridge }), @@ -295,16 +295,16 @@ contract Challenge is DelegateCallAware, IChallenge { } function timeout() external override { - uint256 timeSinceLastMove = block.timestamp - lastMoveTimestamp; + uint256 timeSinceLastMove = block.timestamp - challenge.lastMoveTimestamp; require( timeSinceLastMove > currentResponderTimeLeft(), "TIMEOUT_DEADLINE" ); - if (turn == Turn.ASSERTER) { + if (challenge.turn == Turn.ASSERTER) { emit AsserterTimedOut(); _challengerWin(); - } else if (turn == Turn.CHALLENGER) { + } else if (challenge.turn == Turn.CHALLENGER) { emit ChallengerTimedOut(); _asserterWin(); } else { @@ -314,24 +314,24 @@ contract Challenge is DelegateCallAware, IChallenge { function clearChallenge() external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); - turn = Turn.NO_CHALLENGE; + challenge.turn = Turn.NO_CHALLENGE; } function currentResponder() public view returns (address) { - if (turn == Turn.ASSERTER) { - return asserter; - } else if (turn == Turn.CHALLENGER) { - return challenger; + if (challenge.turn == Turn.ASSERTER) { + return challenge.asserter; + } else if (challenge.turn == Turn.CHALLENGER) { + return challenge.challenger; } else { revert(NO_TURN); } } function currentResponderTimeLeft() public override view returns (uint256) { - if (turn == Turn.ASSERTER) { - return asserterTimeLeft; - } else if (turn == Turn.CHALLENGER) { - return challengerTimeLeft; + if (challenge.turn == Turn.ASSERTER) { + return challenge.asserterTimeLeft; + } else if (challenge.turn == Turn.CHALLENGER) { + return challenge.challengerTimeLeft; } else { revert(NO_TURN); } @@ -344,7 +344,7 @@ contract Challenge is DelegateCallAware, IChallenge { uint256 challengePosition ) internal view returns (uint256 segmentStart, uint256 segmentLength) { require( - challengeStateHash == + challenge.challengeStateHash == ChallengeLib.hashChallengeState( oldSegmentsStart, oldSegmentsLength, @@ -401,7 +401,7 @@ contract Challenge is DelegateCallAware, IChallenge { moduleIdx: 0, functionIdx: 0, functionPc: 0, - modulesRoot: wasmModuleRoot + modulesRoot: challenge.wasmModuleRoot }); return mach.hash(); } @@ -426,19 +426,19 @@ contract Challenge is DelegateCallAware, IChallenge { } function _asserterWin() private { - turn = Turn.NO_CHALLENGE; - resultReceiver.completeChallenge(asserter, challenger); + challenge.turn = Turn.NO_CHALLENGE; + resultReceiver.completeChallenge(challenge.asserter, challenge.challenger); } function _challengerWin() private { - turn = Turn.NO_CHALLENGE; - resultReceiver.completeChallenge(challenger, asserter); + challenge.turn = Turn.NO_CHALLENGE; + resultReceiver.completeChallenge(challenge.challenger, challenge.asserter); } function _currentWin() private { // As a safety measure, challenges can only be resolved by timeouts during mainnet beta. // As state is 0, no move is possible. The other party will lose via timeout - challengeStateHash = bytes32(0); + challenge.challengeStateHash = bytes32(0); // if (turn == Turn.ASSERTER) { // _asserterWin(); diff --git a/solgen/src/challenge/IChallenge.sol b/solgen/src/challenge/IChallenge.sol index c91f7faf7b..ea27f6b37e 100644 --- a/solgen/src/challenge/IChallenge.sol +++ b/solgen/src/challenge/IChallenge.sol @@ -1,6 +1,9 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; +import "../state/GlobalState.sol"; +import "./Challenge.sol"; + interface IChallenge { enum Turn { NO_CHALLENGE, @@ -14,6 +17,24 @@ interface IChallenge { EXECUTION } + struct ChallengeData { + address asserter; + address challenger; + + uint256 asserterTimeLeft; + uint256 challengerTimeLeft; + uint256 lastMoveTimestamp; + + bytes32 wasmModuleRoot; + uint256 maxInboxMessages; + GlobalState[2] startAndEndGlobalStates; + + bytes32 challengeStateHash; + + Turn turn; + ChallengeMode mode; + } + event InitiatedChallenge(); event Bisected( @@ -28,9 +49,11 @@ interface IChallenge { event ExecutionChallengeBegun(uint256 blockSteps); event OneStepProofCompleted(); - function asserter() external view returns (address); - function challenger() external view returns (address); - function lastMoveTimestamp() external view returns (uint256); + function challengeInfo() external view returns (ChallengeData memory); + +// function asserter() external view returns (address); +// function challenger() external view returns (address); +// function lastMoveTimestamp() external view returns (uint256); function currentResponderTimeLeft() external view returns (uint256); function clearChallenge() external; diff --git a/solgen/src/mocks/SingleExecutionChallenge.sol b/solgen/src/mocks/SingleExecutionChallenge.sol index 5af9d4a042..50c26d84f5 100644 --- a/solgen/src/mocks/SingleExecutionChallenge.sol +++ b/solgen/src/mocks/SingleExecutionChallenge.sol @@ -17,17 +17,18 @@ contract SingleExecutionChallenge is Challenge { ) { osp = osp_; resultReceiver = resultReceiver_; - maxInboxMessages = maxInboxMessagesRead_; + challenge.maxInboxMessages = maxInboxMessagesRead_; bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; segments[1] = startAndEndHashes[1]; - challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); - asserter = asserter_; - challenger = challenger_; - asserterTimeLeft = asserterTimeLeft_; - challengerTimeLeft = challengerTimeLeft_; - lastMoveTimestamp = block.timestamp; - turn = Turn.CHALLENGER; + bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); + challenge.challengeStateHash = challengeStateHash; + challenge.asserter = asserter_; + challenge.challenger = challenger_; + challenge.asserterTimeLeft = asserterTimeLeft_; + challenge.challengerTimeLeft = challengerTimeLeft_; + challenge.lastMoveTimestamp = block.timestamp; + challenge.turn = Turn.CHALLENGER; emit Bisected( challengeStateHash, diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index cca6e7b220..2a9b21b897 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -238,21 +238,22 @@ contract ValidatorUtils { IRollupCore rollup, uint64 startIndex, uint64 max - ) external view returns (IChallenge[] memory, bool hasMore) { + ) external view returns (address[] memory, bool hasMore) { (address[] memory stakers, bool hasMoreStakers) = getStakers(rollup, startIndex, max); - IChallenge[] memory challenges = new IChallenge[](stakers.length); + address[] memory challenges = new address[](stakers.length); uint256 index = 0; for (uint256 i = 0; i < stakers.length; i++) { address staker = stakers[i]; - IChallenge challengeAddr = rollup.currentChallenge(staker); - if (challengeAddr != IChallenge(address(0))) { + address challengeAddr = address(rollup.currentChallenge(staker)); + if (challengeAddr != address(0)) { IChallenge challenge = IChallenge(challengeAddr); - uint256 timeSinceLastMove = block.timestamp - challenge.lastMoveTimestamp(); + IChallenge.ChallengeData memory challengeInfo = IChallenge(challengeAddr).challengeInfo(); + uint256 timeSinceLastMove = block.timestamp - challengeInfo.lastMoveTimestamp; if ( timeSinceLastMove > challenge.currentResponderTimeLeft() && - challenge.asserter() == staker + challengeInfo.asserter == staker ) { - challenges[index] = challenge; + challenges[index] = challengeAddr; index++; } } diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 1468df8210..f99a59dabe 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -7,11 +7,7 @@ package validator import ( "context" "fmt" - "math/big" - "strings" - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" @@ -21,6 +17,7 @@ import ( "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/pkg/errors" + "math/big" ) const maxBisectionDegree uint64 = 40 @@ -31,7 +28,7 @@ var challengeBisectedID common.Hash var executionChallengeBegunID common.Hash func init() { - parsedChallengeCoreABI, err := abi.JSON(strings.NewReader(challengegen.ChallengeCoreABI)) + parsedChallengeCoreABI, err := challengegen.ChallengeMetaData.GetAbi() if err != nil { panic(err) } @@ -273,14 +270,14 @@ func (m *ChallengeManager) GetChallengeState(ctx context.Context) (*ChallengeSta if err != nil { return nil, err } - stateHash, err := m.con.ChallengeStateHash(callOpts) + challengeState, err := m.con.Challenge(callOpts) if err != nil { - return nil, err + return nil, errors.WithStack(err) } - if stateHash == (common.Hash{}) { + if challengeState.ChallengeStateHash == (common.Hash{}) { return nil, errors.New("lost challenge (state hash 0)") } - state, err := m.resolveStateHash(ctx, stateHash) + state, err := m.resolveStateHash(ctx, challengeState.ChallengeStateHash) if err != nil { return nil, err } @@ -388,8 +385,8 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { Context: ctx, BlockNumber: latestConfirmedBlock, } - challengeMode, err := m.con.Mode(callOpts) - if err != nil || challengeMode != challengeModeExecution { + challengeState, err := m.con.Challenge(callOpts) + if err != nil || challengeState.Mode != challengeModeExecution { return errors.WithStack(err) } logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ From ba5d5e07a4f49998a690a7c33d9de3e2ceb061ba Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 00:04:02 -0500 Subject: [PATCH 046/110] Refactor args into struct --- solgen/src/challenge/Challenge.sol | 78 +++++++----------------- solgen/src/challenge/IChallenge.sol | 7 +++ validator/block_challenge_backend.go | 10 +-- validator/challenge_manager.go | 10 +-- validator/execution_challenge_backend.go | 10 +-- 5 files changed, 48 insertions(+), 67 deletions(-) diff --git a/solgen/src/challenge/Challenge.sol b/solgen/src/challenge/Challenge.sol index 68fa719b48..b4c39bc20e 100644 --- a/solgen/src/challenge/Challenge.sol +++ b/solgen/src/challenge/Challenge.sol @@ -108,21 +108,10 @@ contract Challenge is DelegateCallAware, IChallenge { * or follows another execution objection */ function bisectExecution( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition, + SegmentSelection calldata selection, bytes32[] calldata newSegments ) external takeTurn { - ( - uint256 challengeStart, - uint256 challengeLength - ) = extractChallengeSegment( - oldSegmentsStart, - oldSegmentsLength, - oldSegments, - challengePosition - ); + (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment(selection); require(challengeLength > 1, "TOO_SHORT"); { uint256 expectedDegree = challengeLength; @@ -133,11 +122,11 @@ contract Challenge is DelegateCallAware, IChallenge { } require( newSegments[newSegments.length - 1] != - oldSegments[challengePosition + 1], + selection.oldSegments[selection.challengePosition + 1], "SAME_END" ); - require(oldSegments[challengePosition] == newSegments[0], "DIFF_START"); + require(selection.oldSegments[selection.challengePosition] == newSegments[0], "DIFF_START"); bytes32 challengeStateHash = ChallengeLib.hashChallengeState( challengeStart, @@ -155,10 +144,7 @@ contract Challenge is DelegateCallAware, IChallenge { } function challengeExecution( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition, + SegmentSelection calldata selection, MachineStatus[2] calldata machineStatuses, bytes32[2] calldata globalStateHashes, uint256 numSteps @@ -169,16 +155,11 @@ contract Challenge is DelegateCallAware, IChallenge { "EXEC_DEADLINE" ); - (uint256 executionChallengeAtSteps, uint256 challengeLength) = extractChallengeSegment( - oldSegmentsStart, - oldSegmentsLength, - oldSegments, - challengePosition - ); + (uint256 executionChallengeAtSteps, uint256 challengeLength) = extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); require( - oldSegments[challengePosition] == + selection.oldSegments[selection.challengePosition] == ChallengeLib.blockStateHash( machineStatuses[0], globalStateHashes[0] @@ -186,7 +167,7 @@ contract Challenge is DelegateCallAware, IChallenge { "WRONG_START" ); require( - oldSegments[challengePosition + 1] != + selection.oldSegments[selection.challengePosition + 1] != ChallengeLib.blockStateHash( machineStatuses[1], globalStateHashes[1] @@ -261,18 +242,10 @@ contract Challenge is DelegateCallAware, IChallenge { } function oneStepProveExecution( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition, + SegmentSelection calldata selection, bytes calldata proof ) external takeTurn { - (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment( - oldSegmentsStart, - oldSegmentsLength, - oldSegments, - challengePosition - ); + (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); bytes32 afterHash = osp.proveOneStep( @@ -282,11 +255,11 @@ contract Challenge is DelegateCallAware, IChallenge { delayedBridge: delayedBridge }), challengeStart, - oldSegments[challengePosition], + selection.oldSegments[selection.challengePosition], proof ); require( - afterHash != oldSegments[challengePosition + 1], + afterHash != selection.oldSegments[selection.challengePosition + 1], "SAME_OSP_END" ); @@ -337,33 +310,28 @@ contract Challenge is DelegateCallAware, IChallenge { } } - function extractChallengeSegment( - uint256 oldSegmentsStart, - uint256 oldSegmentsLength, - bytes32[] calldata oldSegments, - uint256 challengePosition - ) internal view returns (uint256 segmentStart, uint256 segmentLength) { + function extractChallengeSegment(SegmentSelection calldata selection) internal view returns (uint256 segmentStart, uint256 segmentLength) { require( challenge.challengeStateHash == ChallengeLib.hashChallengeState( - oldSegmentsStart, - oldSegmentsLength, - oldSegments + selection.oldSegmentsStart, + selection.oldSegmentsLength, + selection.oldSegments ), "BIS_STATE" ); if ( - oldSegments.length < 2 || - challengePosition >= oldSegments.length - 1 + selection.oldSegments.length < 2 || + selection.challengePosition >= selection.oldSegments.length - 1 ) { revert("BAD_CHALLENGE_POS"); } - uint256 oldChallengeDegree = oldSegments.length - 1; - segmentLength = oldSegmentsLength / oldChallengeDegree; + uint256 oldChallengeDegree = selection.oldSegments.length - 1; + segmentLength = selection.oldSegmentsLength / oldChallengeDegree; // Intentionally done before challengeLength is potentially added to for the final segment - segmentStart = oldSegmentsStart + segmentLength * challengePosition; - if (challengePosition == oldSegments.length - 2) { - segmentLength += oldSegmentsLength % oldChallengeDegree; + segmentStart = selection.oldSegmentsStart + segmentLength * selection.challengePosition; + if (selection.challengePosition == selection.oldSegments.length - 2) { + segmentLength += selection.oldSegmentsLength % oldChallengeDegree; } } diff --git a/solgen/src/challenge/IChallenge.sol b/solgen/src/challenge/IChallenge.sol index ea27f6b37e..e29f4c7cfa 100644 --- a/solgen/src/challenge/IChallenge.sol +++ b/solgen/src/challenge/IChallenge.sol @@ -17,6 +17,13 @@ interface IChallenge { EXECUTION } + struct SegmentSelection { + uint256 oldSegmentsStart; + uint256 oldSegmentsLength; + bytes32[] oldSegments; + uint256 challengePosition; + } + struct ChallengeData { address asserter; address challenger; diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index dd91debeb2..b3703f1059 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -283,10 +283,12 @@ func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client b } return con.ChallengeExecution( auth, - oldState.Start, - new(big.Int).Sub(oldState.End, oldState.Start), - oldState.RawSegments, - big.NewInt(int64(startSegment)), + challengegen.IChallengeSegmentSelection{ + OldSegmentsStart: oldState.Start, + OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), + OldSegments: oldState.RawSegments, + ChallengePosition: big.NewInt(int64(startSegment)), + }, machineStatuses, globalStateHashes, big.NewInt(int64(numsteps)), diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index f99a59dabe..31fdc31733 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -231,10 +231,12 @@ func (m *ChallengeManager) bisect(ctx context.Context, backend ChallengeBackend, } return m.con.BisectExecution( m.auth, - oldState.Start, - new(big.Int).Sub(oldState.End, oldState.Start), - oldState.RawSegments, - big.NewInt(int64(startSegment)), + challengegen.IChallengeSegmentSelection{ + OldSegmentsStart: oldState.Start, + OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), + OldSegments: oldState.RawSegments, + ChallengePosition: big.NewInt(int64(startSegment)), + }, newSegments, ) } diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index fd12d1f068..2b43aeb3a7 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -95,10 +95,12 @@ func (b *ExecutionChallengeBackend) IssueOneStepProof(ctx context.Context, clien proof := mach.ProveNextStep() return con.OneStepProveExecution( auth, - oldState.Start, - new(big.Int).Sub(oldState.End, oldState.Start), - oldState.RawSegments, - big.NewInt(int64(startSegment)), + challengegen.IChallengeSegmentSelection{ + OldSegmentsStart: oldState.Start, + OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), + OldSegments: oldState.RawSegments, + ChallengePosition: big.NewInt(int64(startSegment)), + }, proof, ) } From 03d67f553b86176e05fe54a3c438806e25745431 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 03:18:12 -0500 Subject: [PATCH 047/110] Switch from factory to single manager --- arbnode/node.go | 36 ++- solgen/src/challenge/ChallengeFactory.sol | 47 ---- solgen/src/challenge/ChallengeLib.sol | 92 ++++++++ .../{Challenge.sol => ChallengeManager.sol} | 213 +++++++----------- solgen/src/challenge/IChallenge.sol | 68 ------ solgen/src/challenge/IChallengeFactory.sol | 28 --- solgen/src/challenge/IChallengeManager.sol | 85 +++++++ ...tionChallenge.sol => ExecutionManager.sol} | 8 +- solgen/src/rollup/IRollupCore.sol | 25 +- solgen/src/rollup/RollupAdminLogic.sol | 11 +- solgen/src/rollup/RollupCore.sol | 31 +-- solgen/src/rollup/RollupCreator.sol | 21 +- solgen/src/rollup/RollupLib.sol | 4 +- solgen/src/rollup/RollupUserLogic.sol | 25 +- solgen/src/rollup/ValidatorUtils.sol | 22 +- solgen/src/rollup/ValidatorWallet.sol | 6 +- system_tests/staker_test.go | 2 +- validator/block_challenge_backend.go | 59 +++-- validator/challenge_manager.go | 127 ++++++++--- validator/challenge_test.go | 24 +- validator/execution_challenge_backend.go | 15 +- validator/l1_validator.go | 66 +++--- validator/rollup_watcher.go | 5 +- validator/staker.go | 18 +- validator/validator_utils.go | 8 +- validator/validator_wallet.go | 4 +- 26 files changed, 574 insertions(+), 476 deletions(-) delete mode 100644 solgen/src/challenge/ChallengeFactory.sol rename solgen/src/challenge/{Challenge.sol => ChallengeManager.sol} (64%) delete mode 100644 solgen/src/challenge/IChallenge.sol delete mode 100644 solgen/src/challenge/IChallengeFactory.sol create mode 100644 solgen/src/challenge/IChallengeManager.sol rename solgen/src/mocks/{SingleExecutionChallenge.sol => ExecutionManager.sol} (80%) diff --git a/arbnode/node.go b/arbnode/node.go index dc571cab3a..f7e8178a5b 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -107,44 +107,49 @@ func deployBridgeCreator(ctx context.Context, client arbutil.L1Interface, auth * return bridgeCreatorAddr, nil } -func deployChallengeFactory(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (common.Address, error) { +func deployChallengeFactory( + ctx context.Context, + client arbutil.L1Interface, + auth *bind.TransactOpts, + txTimeout time.Duration, +) (common.Address, common.Address, error) { osp0, tx, _, err := ospgen.DeployOneStepProver0(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("osp0 deploy error: %w", err) } ospMem, _, _, err := ospgen.DeployOneStepProverMemory(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("ospMemory deploy error: %w", err) } ospMath, _, _, err := ospgen.DeployOneStepProverMath(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("ospMath deploy error: %w", err) } ospHostIo, _, _, err := ospgen.DeployOneStepProverHostIo(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("ospHostIo deploy error: %w", err) } ospEntryAddr, tx, _, err := ospgen.DeployOneStepProofEntry(auth, client, osp0, ospMem, ospMath, ospHostIo) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - challengeFactoryAddr, tx, _, err := challengegen.DeployChallengeFactory(auth, client, ospEntryAddr) + challengeManagerAddr, tx, _, err := challengegen.DeployChallengeManager(auth, client) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { - return common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) + return common.Address{}, common.Address{}, fmt.Errorf("ospEntry deploy error: %w", err) } - return challengeFactoryAddr, nil + return ospEntryAddr, challengeManagerAddr, nil } func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth *bind.TransactOpts, txTimeout time.Duration) (*rollupgen.RollupCreator, common.Address, error) { @@ -153,7 +158,7 @@ func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth * return nil, common.Address{}, err } - challengeFactory, err := deployChallengeFactory(ctx, client, auth, txTimeout) + ospEntryAddr, challengeManagerAddr, err := deployChallengeFactory(ctx, client, auth, txTimeout) if err != nil { return nil, common.Address{}, err } @@ -176,7 +181,14 @@ func deployRollupCreator(ctx context.Context, client arbutil.L1Interface, auth * return nil, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) } - tx, err = rollupCreator.SetTemplates(auth, bridgeCreator, challengeFactory, rollupAdminLogic, rollupUserLogic) + tx, err = rollupCreator.SetTemplates( + auth, + bridgeCreator, + ospEntryAddr, + challengeManagerAddr, + rollupAdminLogic, + rollupUserLogic, + ) err = andTxSucceeded(ctx, client, txTimeout, tx, err) if err != nil { return nil, common.Address{}, fmt.Errorf("rollup user logic deploy error: %w", err) @@ -203,7 +215,7 @@ func DeployOnL1(ctx context.Context, l1client arbutil.L1Interface, deployAuth *b if err != nil { return nil, err } - expectedRollupAddr := crypto.CreateAddress(rollupCreatorAddress, nonce+1) + expectedRollupAddr := crypto.CreateAddress(rollupCreatorAddress, nonce+2) tx, err := rollupCreator.CreateRollup( deployAuth, rollupgen.Config{ diff --git a/solgen/src/challenge/ChallengeFactory.sol b/solgen/src/challenge/ChallengeFactory.sol deleted file mode 100644 index dc80d112cd..0000000000 --- a/solgen/src/challenge/ChallengeFactory.sol +++ /dev/null @@ -1,47 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "./Challenge.sol"; -import "./IChallengeFactory.sol"; -import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol"; -import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; - -contract ChallengeFactory is IChallengeFactory { - UpgradeableBeacon public beacon; - IOneStepProofEntry public osp; - - constructor(IOneStepProofEntry osp_) { - osp = osp_; - address challengeTemplate = address(new Challenge()); - beacon = new UpgradeableBeacon(challengeTemplate); - beacon.transferOwnership(msg.sender); - } - - function createChallenge( - ChallengeContracts calldata contractAddresses, - bytes32 wasmModuleRoot_, - MachineStatus[2] calldata startAndEndMachineStatuses_, - GlobalState[2] calldata startAndEndGlobalStates_, - uint64 numBlocks, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) external override returns (IChallenge) { - address clone = address(new BeaconProxy(address(beacon), "")); - Challenge(clone).initialize( - osp, - contractAddresses, - wasmModuleRoot_, - startAndEndMachineStatuses_, - startAndEndGlobalStates_, - numBlocks, - asserter_, - challenger_, - asserterTimeLeft_, - challengerTimeLeft_ - ); - emit ChallengeCreated(IChallenge(clone)); - return IChallenge(clone); - } -} diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 702916e805..22b286d4c7 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -4,6 +4,98 @@ pragma solidity ^0.8.0; import "../state/Machine.sol"; library ChallengeLib { + using MachineLib for Machine; + + struct SegmentSelection { + uint256 oldSegmentsStart; + uint256 oldSegmentsLength; + bytes32[] oldSegments; + uint256 challengePosition; + } + + function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) + internal + pure + returns (bytes32) + { + ValueStack memory values; + { + // Start the value stack with the function call ABI for the entrypoint + Value[] memory startingValues = new Value[](3); + startingValues[0] = ValueLib.newRefNull(); + startingValues[1] = ValueLib.newI32(0); + startingValues[2] = ValueLib.newI32(0); + ValueArray memory valuesArray = ValueArray({ + inner: startingValues + }); + values = ValueStack({ + proved: valuesArray, + remainingHash: 0 + }); + } + ValueStack memory internalStack; + PcStack memory blocks; + StackFrameWindow memory frameStack; + + Machine memory mach = Machine({ + status: MachineStatus.RUNNING, + valueStack: values, + internalStack: internalStack, + blockStack: blocks, + frameStack: frameStack, + globalStateHash: globalStateHash, + moduleIdx: 0, + functionIdx: 0, + functionPc: 0, + modulesRoot: wasmModuleRoot + }); + return mach.hash(); + } + + function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) + internal + pure + returns (bytes32) + { + if (status == MachineStatus.FINISHED) { + return + keccak256( + abi.encodePacked("Machine finished:", globalStateHash) + ); + } else if (status == MachineStatus.ERRORED) { + return keccak256(abi.encodePacked("Machine errored:")); + } else if (status == MachineStatus.TOO_FAR) { + return keccak256(abi.encodePacked("Machine too far:")); + } else { + revert("BAD_BLOCK_STATUS"); + } + } + + function extractChallengeSegment(bytes32 currentStateHash, SegmentSelection calldata selection) internal pure returns (uint256 segmentStart, uint256 segmentLength) { + require( + currentStateHash == + ChallengeLib.hashChallengeState( + selection.oldSegmentsStart, + selection.oldSegmentsLength, + selection.oldSegments + ), + "BIS_STATE" + ); + if ( + selection.oldSegments.length < 2 || + selection.challengePosition >= selection.oldSegments.length - 1 + ) { + revert("BAD_CHALLENGE_POS"); + } + uint256 oldChallengeDegree = selection.oldSegments.length - 1; + segmentLength = selection.oldSegmentsLength / oldChallengeDegree; + // Intentionally done before challengeLength is potentially added to for the final segment + segmentStart = selection.oldSegmentsStart + segmentLength * selection.challengePosition; + if (selection.challengePosition == selection.oldSegments.length - 2) { + segmentLength += selection.oldSegmentsLength % oldChallengeDegree; + } + } + function hashChallengeState( uint256 segmentsStart, uint256 segmentsLength, diff --git a/solgen/src/challenge/Challenge.sol b/solgen/src/challenge/ChallengeManager.sol similarity index 64% rename from solgen/src/challenge/Challenge.sol rename to solgen/src/challenge/ChallengeManager.sol index b4c39bc20e..35f5dca4c7 100644 --- a/solgen/src/challenge/Challenge.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -6,17 +6,17 @@ import "../osp/IOneStepProofEntry.sol"; import "../state/GlobalState.sol"; import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; -import "./IChallenge.sol"; -import "./IChallengeFactory.sol"; +import "./IChallengeManager.sol"; -contract Challenge is DelegateCallAware, IChallenge { +contract ChallengeManager is DelegateCallAware, IChallengeManager { using GlobalStateLib for GlobalState; using MachineLib for Machine; string constant NO_TURN = "NO_TURN"; uint256 constant MAX_CHALLENGE_DEGREE = 40; - ChallengeData public challenge; + uint64 totalChallengesCreated; + mapping (uint256 => Challenge) public challenges; IChallengeResultReceiver public resultReceiver; @@ -24,14 +24,15 @@ contract Challenge is DelegateCallAware, IChallenge { IBridge public delayedBridge; IOneStepProofEntry public osp; - function challengeInfo() external view override returns (ChallengeData memory) { - return challenge; + function challengeInfo(uint64 challengeIndex) external view override returns (Challenge memory) { + return challenges[challengeIndex]; } - modifier takeTurn() { - require(msg.sender == currentResponder(), "BIS_SENDER"); + modifier takeTurn(uint64 challengeIndex) { + Challenge storage challenge = challenges[challengeIndex]; + require(msg.sender == currentResponder(challengeIndex), "BIS_SENDER"); require( - block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(), + block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(challengeIndex), "BIS_DEADLINE" ); @@ -47,10 +48,21 @@ contract Challenge is DelegateCallAware, IChallenge { challenge.lastMoveTimestamp = block.timestamp; } - // contractAddresses = [ resultReceiver, sequencerInbox, delayedBridge ] function initialize( - IOneStepProofEntry osp_, - IChallengeFactory.ChallengeContracts calldata contractAddresses, + IChallengeResultReceiver resultReceiver_, + ISequencerInbox sequencerInbox_, + IBridge delayedBridge_, + IOneStepProofEntry osp_ + ) external override onlyDelegated { + require(address(resultReceiver) == address(0), "ALREADY_INIT"); + require(address(resultReceiver_) != address(0), "NO_RESULT_RECEIVER"); + resultReceiver = resultReceiver_; + sequencerInbox = sequencerInbox_; + delayedBridge = delayedBridge_; + osp = osp_; + } + + function createChallenge( bytes32 wasmModuleRoot_, MachineStatus[2] calldata startAndEndMachineStatuses_, GlobalState[2] calldata startAndEndGlobalStates_, @@ -59,19 +71,14 @@ contract Challenge is DelegateCallAware, IChallenge { address challenger_, uint256 asserterTimeLeft_, uint256 challengerTimeLeft_ - ) external onlyDelegated { - require(address(resultReceiver) == address(0), "ALREADY_INIT"); - require(address(contractAddresses.resultReceiver) != address(0), "NO_RESULT_RECEIVER"); - resultReceiver = IChallengeResultReceiver(contractAddresses.resultReceiver); - sequencerInbox = ISequencerInbox(contractAddresses.sequencerInbox); - delayedBridge = IBridge(contractAddresses.delayedBridge); - osp = osp_; - + ) external override returns (uint64) { bytes32[] memory segments = new bytes32[](2); segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); segments[1] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[1], startAndEndGlobalStates_[1].hash()); bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numBlocks, segments); + uint64 challengeIndex = ++totalChallengesCreated; + Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; // No need to set maxInboxMessages until execution challenge challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; @@ -85,20 +92,24 @@ contract Challenge is DelegateCallAware, IChallenge { challenge.mode = ChallengeMode.BLOCK; challenge.challengeStateHash = challengeStateHash; - emit InitiatedChallenge(); + emit InitiatedChallenge(challengeIndex); emit Bisected( + challengeIndex, challengeStateHash, 0, numBlocks, segments ); + return challengeIndex; } - function getStartGlobalState() external view returns (GlobalState memory) { + function getStartGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { + Challenge storage challenge = challenges[challengeIndex]; return challenge.startAndEndGlobalStates[0]; } - function getEndGlobalState() external view returns (GlobalState memory) { + function getEndGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { + Challenge storage challenge = challenges[challengeIndex]; return challenge.startAndEndGlobalStates[1]; } @@ -108,10 +119,12 @@ contract Challenge is DelegateCallAware, IChallenge { * or follows another execution objection */ function bisectExecution( - SegmentSelection calldata selection, + uint64 challengeIndex, + ChallengeLib.SegmentSelection calldata selection, bytes32[] calldata newSegments - ) external takeTurn { - (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment(selection); + ) external takeTurn(challengeIndex) { + Challenge storage challenge = challenges[challengeIndex]; + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); require(challengeLength > 1, "TOO_SHORT"); { uint256 expectedDegree = challengeLength; @@ -136,6 +149,7 @@ contract Challenge is DelegateCallAware, IChallenge { challenge.challengeStateHash = challengeStateHash; emit Bisected( + challengeIndex, challengeStateHash, challengeStart, challengeLength, @@ -144,18 +158,20 @@ contract Challenge is DelegateCallAware, IChallenge { } function challengeExecution( - SegmentSelection calldata selection, + uint64 challengeIndex, + ChallengeLib.SegmentSelection calldata selection, MachineStatus[2] calldata machineStatuses, bytes32[2] calldata globalStateHashes, uint256 numSteps ) external { - require(msg.sender == currentResponder(), "EXEC_SENDER"); + Challenge storage challenge = challenges[challengeIndex]; + require(msg.sender == currentResponder(challengeIndex), "EXEC_SENDER"); require( - block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(), + block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(challengeIndex), "EXEC_DEADLINE" ); - (uint256 executionChallengeAtSteps, uint256 challengeLength) = extractChallengeSegment(selection); + (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); require(challengeLength == 1, "TOO_LONG"); require( @@ -182,7 +198,7 @@ contract Challenge is DelegateCallAware, IChallenge { globalStateHashes[0] == globalStateHashes[1], "HALTED_CHANGE" ); - _currentWin(); + _currentWin(challenge); return; } @@ -192,10 +208,11 @@ contract Challenge is DelegateCallAware, IChallenge { } bytes32[2] memory startAndEndHashes; - startAndEndHashes[0] = getStartMachineHash( - globalStateHashes[0] + startAndEndHashes[0] = ChallengeLib.getStartMachineHash( + globalStateHashes[0], + challenge.wasmModuleRoot ); - startAndEndHashes[1] = getEndMachineHash( + startAndEndHashes[1] = ChallengeLib.getEndMachineHash( machineStatuses[1], globalStateHashes[1] ); @@ -205,7 +222,6 @@ contract Challenge is DelegateCallAware, IChallenge { maxInboxMessagesRead++; } - if (challenge.turn == Turn.CHALLENGER) { (challenge.asserter, challenge.challenger) = (challenge.challenger, challenge.asserter); ( @@ -230,22 +246,24 @@ contract Challenge is DelegateCallAware, IChallenge { challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.EXECUTION; - emit InitiatedChallenge(); emit Bisected( + challengeIndex, challengeStateHash, 0, numSteps, segments ); - emit ExecutionChallengeBegun(executionChallengeAtSteps); + emit ExecutionChallengeBegun(challengeIndex, executionChallengeAtSteps); } function oneStepProveExecution( - SegmentSelection calldata selection, + uint64 challengeIndex, + ChallengeLib.SegmentSelection calldata selection, bytes calldata proof - ) external takeTurn { - (uint256 challengeStart, uint256 challengeLength) = extractChallengeSegment(selection); + ) external takeTurn(challengeIndex) { + Challenge storage challenge = challenges[challengeIndex]; + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); require(challengeLength == 1, "TOO_LONG"); bytes32 afterHash = osp.proveOneStep( @@ -263,34 +281,37 @@ contract Challenge is DelegateCallAware, IChallenge { "SAME_OSP_END" ); - emit OneStepProofCompleted(); - _currentWin(); + emit OneStepProofCompleted(challengeIndex); + _currentWin(challenge); } - function timeout() external override { + function timeout(uint64 challengeIndex) external override { + Challenge storage challenge = challenges[challengeIndex]; uint256 timeSinceLastMove = block.timestamp - challenge.lastMoveTimestamp; require( - timeSinceLastMove > currentResponderTimeLeft(), + timeSinceLastMove > currentResponderTimeLeft(challengeIndex), "TIMEOUT_DEADLINE" ); if (challenge.turn == Turn.ASSERTER) { - emit AsserterTimedOut(); - _challengerWin(); + emit AsserterTimedOut(challengeIndex); + _challengerWin(challenge); } else if (challenge.turn == Turn.CHALLENGER) { - emit ChallengerTimedOut(); - _asserterWin(); + emit ChallengerTimedOut(challengeIndex); + _asserterWin(challenge); } else { revert(NO_TURN); } } - function clearChallenge() external override { + function clearChallenge(uint64 challengeIndex) external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); + Challenge storage challenge = challenges[challengeIndex]; challenge.turn = Turn.NO_CHALLENGE; } - function currentResponder() public view returns (address) { + function currentResponder(uint64 challengeIndex) public view returns (address) { + Challenge storage challenge = challenges[challengeIndex]; if (challenge.turn == Turn.ASSERTER) { return challenge.asserter; } else if (challenge.turn == Turn.CHALLENGER) { @@ -300,7 +321,8 @@ contract Challenge is DelegateCallAware, IChallenge { } } - function currentResponderTimeLeft() public override view returns (uint256) { + function currentResponderTimeLeft(uint64 challengeIndex) public override view returns (uint256) { + Challenge storage challenge = challenges[challengeIndex]; if (challenge.turn == Turn.ASSERTER) { return challenge.asserterTimeLeft; } else if (challenge.turn == Turn.CHALLENGER) { @@ -310,100 +332,17 @@ contract Challenge is DelegateCallAware, IChallenge { } } - function extractChallengeSegment(SegmentSelection calldata selection) internal view returns (uint256 segmentStart, uint256 segmentLength) { - require( - challenge.challengeStateHash == - ChallengeLib.hashChallengeState( - selection.oldSegmentsStart, - selection.oldSegmentsLength, - selection.oldSegments - ), - "BIS_STATE" - ); - if ( - selection.oldSegments.length < 2 || - selection.challengePosition >= selection.oldSegments.length - 1 - ) { - revert("BAD_CHALLENGE_POS"); - } - uint256 oldChallengeDegree = selection.oldSegments.length - 1; - segmentLength = selection.oldSegmentsLength / oldChallengeDegree; - // Intentionally done before challengeLength is potentially added to for the final segment - segmentStart = selection.oldSegmentsStart + segmentLength * selection.challengePosition; - if (selection.challengePosition == selection.oldSegments.length - 2) { - segmentLength += selection.oldSegmentsLength % oldChallengeDegree; - } - } - - function getStartMachineHash(bytes32 globalStateHash) - internal - view - returns (bytes32) - { - ValueStack memory values; - { - // Start the value stack with the function call ABI for the entrypoint - Value[] memory startingValues = new Value[](3); - startingValues[0] = ValueLib.newRefNull(); - startingValues[1] = ValueLib.newI32(0); - startingValues[2] = ValueLib.newI32(0); - ValueArray memory valuesArray = ValueArray({ - inner: startingValues - }); - values = ValueStack({ - proved: valuesArray, - remainingHash: 0 - }); - } - ValueStack memory internalStack; - PcStack memory blocks; - StackFrameWindow memory frameStack; - - Machine memory mach = Machine({ - status: MachineStatus.RUNNING, - valueStack: values, - internalStack: internalStack, - blockStack: blocks, - frameStack: frameStack, - globalStateHash: globalStateHash, - moduleIdx: 0, - functionIdx: 0, - functionPc: 0, - modulesRoot: challenge.wasmModuleRoot - }); - return mach.hash(); - } - - function getEndMachineHash(MachineStatus status, bytes32 globalStateHash) - internal - pure - returns (bytes32) - { - if (status == MachineStatus.FINISHED) { - return - keccak256( - abi.encodePacked("Machine finished:", globalStateHash) - ); - } else if (status == MachineStatus.ERRORED) { - return keccak256(abi.encodePacked("Machine errored:")); - } else if (status == MachineStatus.TOO_FAR) { - return keccak256(abi.encodePacked("Machine too far:")); - } else { - revert("BAD_BLOCK_STATUS"); - } - } - - function _asserterWin() private { + function _asserterWin(Challenge storage challenge) private { challenge.turn = Turn.NO_CHALLENGE; resultReceiver.completeChallenge(challenge.asserter, challenge.challenger); } - function _challengerWin() private { + function _challengerWin(Challenge storage challenge) private { challenge.turn = Turn.NO_CHALLENGE; resultReceiver.completeChallenge(challenge.challenger, challenge.asserter); } - function _currentWin() private { + function _currentWin(Challenge storage challenge) private { // As a safety measure, challenges can only be resolved by timeouts during mainnet beta. // As state is 0, no move is possible. The other party will lose via timeout challenge.challengeStateHash = bytes32(0); diff --git a/solgen/src/challenge/IChallenge.sol b/solgen/src/challenge/IChallenge.sol deleted file mode 100644 index e29f4c7cfa..0000000000 --- a/solgen/src/challenge/IChallenge.sol +++ /dev/null @@ -1,68 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "../state/GlobalState.sol"; -import "./Challenge.sol"; - -interface IChallenge { - enum Turn { - NO_CHALLENGE, - ASSERTER, - CHALLENGER - } - - enum ChallengeMode { - NONE, - BLOCK, - EXECUTION - } - - struct SegmentSelection { - uint256 oldSegmentsStart; - uint256 oldSegmentsLength; - bytes32[] oldSegments; - uint256 challengePosition; - } - - struct ChallengeData { - address asserter; - address challenger; - - uint256 asserterTimeLeft; - uint256 challengerTimeLeft; - uint256 lastMoveTimestamp; - - bytes32 wasmModuleRoot; - uint256 maxInboxMessages; - GlobalState[2] startAndEndGlobalStates; - - bytes32 challengeStateHash; - - Turn turn; - ChallengeMode mode; - } - - event InitiatedChallenge(); - - event Bisected( - bytes32 indexed challengeRoot, - uint256 challengedSegmentStart, - uint256 challengedSegmentLength, - bytes32[] chainHashes - ); - event AsserterTimedOut(); - event ChallengerTimedOut(); - - event ExecutionChallengeBegun(uint256 blockSteps); - event OneStepProofCompleted(); - - function challengeInfo() external view returns (ChallengeData memory); - -// function asserter() external view returns (address); -// function challenger() external view returns (address); -// function lastMoveTimestamp() external view returns (uint256); - function currentResponderTimeLeft() external view returns (uint256); - - function clearChallenge() external; - function timeout() external; -} diff --git a/solgen/src/challenge/IChallengeFactory.sol b/solgen/src/challenge/IChallengeFactory.sol deleted file mode 100644 index d1f3a94a27..0000000000 --- a/solgen/src/challenge/IChallengeFactory.sol +++ /dev/null @@ -1,28 +0,0 @@ -//SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "../osp/IOneStepProofEntry.sol"; -import "./IChallenge.sol"; -import "./IChallengeResultReceiver.sol"; - -interface IChallengeFactory { - event ChallengeCreated(IChallenge challenge); - - struct ChallengeContracts { - IChallengeResultReceiver resultReceiver; - ISequencerInbox sequencerInbox; - IBridge delayedBridge; - } - - function createChallenge( - ChallengeContracts calldata contractAddresses, - bytes32 wasmModuleRoot_, - MachineStatus[2] memory startAndEndMachineStatuses_, - GlobalState[2] memory startAndEndGlobalStates_, - uint64 numBlocks, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) external returns (IChallenge); -} diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol new file mode 100644 index 0000000000..0033c4e28f --- /dev/null +++ b/solgen/src/challenge/IChallengeManager.sol @@ -0,0 +1,85 @@ +//SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import "../state/GlobalState.sol"; +import "../state/Machine.sol"; +import "../bridge/IBridge.sol"; +import "../bridge/ISequencerInbox.sol"; +import "../osp/IOneStepProofEntry.sol"; + +import "./IChallengeResultReceiver.sol"; + +interface IChallengeManager { + enum Turn { + NO_CHALLENGE, + ASSERTER, + CHALLENGER + } + + enum ChallengeMode { + NONE, + BLOCK, + EXECUTION + } + + struct Challenge { + address asserter; + address challenger; + + uint256 asserterTimeLeft; + uint256 challengerTimeLeft; + uint256 lastMoveTimestamp; + + bytes32 wasmModuleRoot; + uint256 maxInboxMessages; + GlobalState[2] startAndEndGlobalStates; + + bytes32 challengeStateHash; + + Turn turn; + ChallengeMode mode; + } + + event InitiatedChallenge(uint64 indexed challengeIndex); + + event Bisected( + uint64 indexed challengeIndex, + bytes32 indexed challengeRoot, + uint256 challengedSegmentStart, + uint256 challengedSegmentLength, + bytes32[] chainHashes + ); + event AsserterTimedOut(uint64 indexed challengeIndex); + event ChallengerTimedOut(uint64 indexed challengeIndex); + + event ExecutionChallengeBegun(uint64 indexed challengeIndex, uint256 blockSteps); + event OneStepProofCompleted(uint64 indexed challengeIndex); + + function initialize( + IChallengeResultReceiver resultReceiver_, + ISequencerInbox sequencerInbox_, + IBridge delayedBridge_, + IOneStepProofEntry osp_ + ) external; + + function createChallenge( + bytes32 wasmModuleRoot_, + MachineStatus[2] calldata startAndEndMachineStatuses_, + GlobalState[2] calldata startAndEndGlobalStates_, + uint64 numBlocks, + address asserter_, + address challenger_, + uint256 asserterTimeLeft_, + uint256 challengerTimeLeft_ + ) external returns (uint64); + + function challengeInfo(uint64 challengeIndex_) external view returns (Challenge memory); + +// function asserter() external view returns (address); +// function challenger() external view returns (address); +// function lastMoveTimestamp() external view returns (uint256); + function currentResponderTimeLeft(uint64 challengeIndex_) external view returns (uint256); + + function clearChallenge(uint64 challengeIndex_) external; + function timeout(uint64 challengeIndex_) external; +} diff --git a/solgen/src/mocks/SingleExecutionChallenge.sol b/solgen/src/mocks/ExecutionManager.sol similarity index 80% rename from solgen/src/mocks/SingleExecutionChallenge.sol rename to solgen/src/mocks/ExecutionManager.sol index 50c26d84f5..a4d383a38f 100644 --- a/solgen/src/mocks/SingleExecutionChallenge.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -1,9 +1,9 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../challenge/Challenge.sol"; +import "../challenge/ChallengeManager.sol"; -contract SingleExecutionChallenge is Challenge { +contract SingleExecutionChallenge is ChallengeManager { constructor( IOneStepProofEntry osp_, IChallengeResultReceiver resultReceiver_, @@ -17,6 +17,8 @@ contract SingleExecutionChallenge is Challenge { ) { osp = osp_; resultReceiver = resultReceiver_; + uint64 challengeIndex = ++totalChallengesCreated; + Challenge storage challenge = challenges[challengeIndex]; challenge.maxInboxMessages = maxInboxMessagesRead_; bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; @@ -29,8 +31,10 @@ contract SingleExecutionChallenge is Challenge { challenge.challengerTimeLeft = challengerTimeLeft_; challenge.lastMoveTimestamp = block.timestamp; challenge.turn = Turn.CHALLENGER; + challenge.mode = ChallengeMode.EXECUTION; emit Bisected( + challengeIndex, challengeStateHash, 0, numSteps_, diff --git a/solgen/src/rollup/IRollupCore.sol b/solgen/src/rollup/IRollupCore.sol index 766f1e357e..b7077da2bb 100644 --- a/solgen/src/rollup/IRollupCore.sol +++ b/solgen/src/rollup/IRollupCore.sol @@ -21,20 +21,17 @@ pragma solidity ^0.8.0; import "./Node.sol"; import "./RollupLib.sol"; -import "../challenge/IChallenge.sol"; import "../osp/IOneStepProofEntry.sol"; interface IRollupCore { - function _stakerMap(address stakerAddress) - external - view - returns ( - uint64, - uint64, - uint256, - IChallenge, - bool - ); + struct Staker { + uint256 amountStaked; + uint64 index; + uint64 latestStakedNode; + // currentChallenge is 0 if staker is not in a challenge + uint64 currentChallenge; + bool isStaked; + } event RollupInitialized(bytes32 machineHash, uint256 chainId); @@ -57,7 +54,7 @@ interface IRollupCore { event NodeRejected(uint64 indexed nodeNum); event RollupChallengeStarted( - IChallenge indexed challengeContract, + uint64 indexed challengeIndex, address asserter, address challenger, uint64 challengedNode @@ -71,6 +68,8 @@ interface IRollupCore { uint256 finalBalance ); + function challengeManager() external view returns (IChallengeManager); + /** * @notice Get the Node for the given index. */ @@ -107,7 +106,7 @@ interface IRollupCore { * @param staker Staker address to lookup * @return Current challenge of the staker */ - function currentChallenge(address staker) external view returns (IChallenge); + function currentChallenge(address staker) external view returns (uint64); /** * @notice Get the amount staked of the given staker diff --git a/solgen/src/rollup/RollupAdminLogic.sol b/solgen/src/rollup/RollupAdminLogic.sol index 26071d796e..6bfec328f5 100644 --- a/solgen/src/rollup/RollupAdminLogic.sol +++ b/solgen/src/rollup/RollupAdminLogic.sol @@ -6,7 +6,7 @@ import { IRollupAdmin, IRollupUser } from "./IRollupLogic.sol"; import "./RollupCore.sol"; import "../bridge/IOutbox.sol"; import "../bridge/ISequencerInbox.sol"; -import "../challenge/IChallenge.sol"; +import "../challenge/IChallengeManager.sol"; import "../libraries/SecondaryLogicUUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; @@ -25,7 +25,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade rollupEventBridge.rollupInitialized(config.owner, config.chainId); sequencerBridge.addSequencerL2Batch(0, "", 1, IGasRefunder(address(0))); - challengeFactory = connectedContracts.challengeFactory; + challengeManager = connectedContracts.challengeManager; Node memory node = createInitialNode(); initializeCore(node); @@ -261,13 +261,12 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade { require(stakerA.length == stakerB.length, "WRONG_LENGTH"); for (uint256 i = 0; i < stakerA.length; i++) { - IChallenge chall = inChallenge(stakerA[i], stakerB[i]); + uint64 chall = inChallenge(stakerA[i], stakerB[i]); - require(address(0) != address(chall), "NOT_IN_CHALL"); + require(chall != 0, "NOT_IN_CHALL"); clearChallenge(stakerA[i]); clearChallenge(stakerB[i]); - - chall.clearChallenge(); + challengeManager.clearChallenge(chall); } emit OwnerFunctionCalled(21); } diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 183c21fba6..c553cc7517 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -26,7 +26,7 @@ import "./RollupLib.sol"; import "./RollupEventBridge.sol"; import "./IRollupCore.sol"; -import "../challenge/IChallengeFactory.sol"; +import "../challenge/IChallengeManager.sol"; import "../bridge/ISequencerInbox.sol"; import "../bridge/IBridge.sol"; @@ -47,7 +47,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { ISequencerInbox public sequencerBridge; IOutbox public outbox; RollupEventBridge public rollupEventBridge; - IChallengeFactory public challengeFactory; + IChallengeManager public override challengeManager; // when a staker loses a challenge, half of their funds get escrowed in this address address public loserStakeEscrow; address public stakeToken; @@ -61,14 +61,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { uint64 latestStakedNode; } - struct Staker { - uint64 index; - uint64 latestStakedNode; - uint256 amountStaked; - // currentChallenge is 0 if staker is not in a challenge - IChallenge currentChallenge; - bool isStaked; - } + uint64 private _latestConfirmed; uint64 private _firstUnresolvedNode; @@ -78,7 +71,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { mapping(uint64 => mapping(address => bool)) private _nodeStakers; address[] private _stakerList; - mapping(address => Staker) public override _stakerMap; + mapping(address => Staker) public _stakerMap; Zombie[] private _zombies; @@ -168,7 +161,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { public view override - returns (IChallenge) + returns (uint64) { return _stakerMap[staker].currentChallenge; } @@ -328,10 +321,10 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { uint64 stakerIndex = uint64(_stakerList.length); _stakerList.push(stakerAddress); _stakerMap[stakerAddress] = Staker( + depositAmount, stakerIndex, _latestConfirmed, - depositAmount, - IChallenge(address(0)), // new staker is not in challenge + 0, // new staker is not in challenge true ); _lastStakeBlock = uint64(block.number); @@ -347,12 +340,12 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { function inChallenge(address stakerAddress1, address stakerAddress2) internal view - returns (IChallenge) + returns (uint64) { Staker storage staker1 = _stakerMap[stakerAddress1]; Staker storage staker2 = _stakerMap[stakerAddress2]; - IChallenge challenge = staker1.currentChallenge; - require(address(challenge) != address(0), "NO_CHAL"); + uint64 challenge = staker1.currentChallenge; + require(challenge != 0, "NO_CHAL"); require(challenge == staker2.currentChallenge, "DIFF_IN_CHAL"); return challenge; } @@ -363,7 +356,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { */ function clearChallenge(address stakerAddress) internal { Staker storage staker = _stakerMap[stakerAddress]; - staker.currentChallenge = IChallenge(address(0)); + staker.currentChallenge = 0; } /** @@ -375,7 +368,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { function challengeStarted( address staker1, address staker2, - IChallenge challenge + uint64 challenge ) internal { _stakerMap[staker1].currentChallenge = challenge; _stakerMap[staker2].currentChallenge = challenge; diff --git a/solgen/src/rollup/RollupCreator.sol b/solgen/src/rollup/RollupCreator.sol index b6b8894175..40b2eb3fa6 100644 --- a/solgen/src/rollup/RollupCreator.sol +++ b/solgen/src/rollup/RollupCreator.sol @@ -41,7 +41,8 @@ contract RollupCreator is Ownable { event TemplatesUpdated(); BridgeCreator public bridgeCreator; - IChallengeFactory public challengeFactory; + IOneStepProofEntry public osp; + IChallengeManager public challengeManagerTemplate; IRollupAdmin public rollupAdminLogic; IRollupUser public rollupUserLogic; @@ -49,12 +50,14 @@ contract RollupCreator is Ownable { function setTemplates( BridgeCreator _bridgeCreator, - IChallengeFactory _challengeFactory, + IOneStepProofEntry _osp, + IChallengeManager _challengeManagerLogic, IRollupAdmin _rollupAdminLogic, IRollupUser _rollupUserLogic ) external onlyOwner { bridgeCreator = _bridgeCreator; - challengeFactory = _challengeFactory; + osp = _osp; + challengeManagerTemplate = _challengeManagerLogic; rollupAdminLogic = _rollupAdminLogic; rollupUserLogic = _rollupUserLogic; emit TemplatesUpdated(); @@ -89,6 +92,16 @@ contract RollupCreator is Ownable { frame.admin.transferOwnership(config.owner); + IChallengeManager challengeManager = IChallengeManager(address( + new TransparentUpgradeableProxy(address(challengeManagerTemplate), address(frame.admin), "") + )); + challengeManager.initialize( + IChallengeResultReceiver(expectedRollupAddr), + frame.sequencerInbox, + frame.delayedBridge, + osp + ); + frame.rollup = new ArbitrumProxy( config, ContractDependencies({ @@ -96,7 +109,7 @@ contract RollupCreator is Ownable { sequencerInbox: frame.sequencerInbox, outbox: frame.outbox, rollupEventBridge: frame.rollupEventBridge, - challengeFactory: challengeFactory, + challengeManager: challengeManager, rollupAdminLogic: rollupAdminLogic, rollupUserLogic: rollupUserLogic }) diff --git a/solgen/src/rollup/RollupLib.sol b/solgen/src/rollup/RollupLib.sol index 55971abe17..e28d59027a 100644 --- a/solgen/src/rollup/RollupLib.sol +++ b/solgen/src/rollup/RollupLib.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.0; -import "../challenge/IChallengeFactory.sol"; +import "../challenge/IChallengeManager.sol"; import "../challenge/ChallengeLib.sol"; import "../state/GlobalState.sol"; import "../bridge/ISequencerInbox.sol"; @@ -45,7 +45,7 @@ struct ContractDependencies { ISequencerInbox sequencerInbox; IOutbox outbox; RollupEventBridge rollupEventBridge; - IChallengeFactory challengeFactory; + IChallengeManager challengeManager; IRollupAdmin rollupAdminLogic; IRollupUser rollupUserLogic; diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index a0a6dc16da..4a15d951ca 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -335,7 +335,7 @@ abstract contract AbsRollupUserLogic is return; } // Start a challenge between staker1 and staker2. Staker1 will defend the correctness of node1, and staker2 will challenge it. - IChallenge challengeAddress = createChallengeHelper( + uint64 challengeIndex = createChallengeHelper( stakers, machineStatuses, globalStates, @@ -345,10 +345,10 @@ abstract contract AbsRollupUserLogic is commonEndTime - proposedTimes[1] ); // trusted external call - challengeStarted(stakers[0], stakers[1], challengeAddress); + challengeStarted(stakers[0], stakers[1], challengeIndex); emit RollupChallengeStarted( - challengeAddress, + challengeIndex, stakers[0], stakers[1], nodeNums[0] @@ -363,14 +363,9 @@ abstract contract AbsRollupUserLogic is bytes32[2] calldata wasmModuleRoots, uint256 asserterTimeLeft, uint256 challengerTimeLeft - ) internal returns (IChallenge) { + ) internal returns (uint64) { return - challengeFactory.createChallenge( - IChallengeFactory.ChallengeContracts({ - resultReceiver: this, - sequencerInbox: sequencerBridge, - delayedBridge: delayedBridge - }), + challengeManager.createChallenge( wasmModuleRoots[0], machineStatuses, globalStates, @@ -392,12 +387,8 @@ abstract contract AbsRollupUserLogic is override whenNotPaused { - // Only the challenge contract can call this to declare the winner and loser - require( - msg.sender == address(inChallenge(winningStaker, losingStaker)), - "WRONG_SENDER" - ); - + // Only the challenge manager contract can call this to declare the winner and loser + require(msg.sender == address(challengeManager), "WRONG_SENDER"); completeChallengeImpl(winningStaker, losingStaker); } @@ -621,7 +612,7 @@ abstract contract AbsRollupUserLogic is function requireUnchallengedStaker(address stakerAddress) private view { require(isStaked(stakerAddress), "NOT_STAKED"); require( - address(currentChallenge(stakerAddress)) == address(0), + currentChallenge(stakerAddress) == 0, "IN_CHAL" ); } diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index 2a9b21b897..efe5a36d4b 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2; import "../rollup/IRollupCore.sol"; import "../rollup/IRollupLogic.sol"; -import "../challenge/IChallenge.sol"; +import "../challenge/IChallengeManager.sol"; contract ValidatorUtils { using NodeLib for Node; @@ -47,7 +47,7 @@ contract ValidatorUtils { bool isStaked, uint64 latestStakedNode, uint256 amountStaked, - IChallenge currentChallenge + uint64 currentChallenge ) { return ( @@ -140,7 +140,7 @@ contract ValidatorUtils { address staker = rollup.getStakerAddress(i); uint256 latestStakedNode = rollup.latestStakedNode(staker); if ( - latestStakedNode <= latestConfirmed && rollup.currentChallenge(staker) == IChallenge(address(0)) + latestStakedNode <= latestConfirmed && rollup.currentChallenge(staker) == 0 ) { stakers[index] = staker; index++; @@ -238,22 +238,22 @@ contract ValidatorUtils { IRollupCore rollup, uint64 startIndex, uint64 max - ) external view returns (address[] memory, bool hasMore) { + ) external view returns (uint64[] memory, bool hasMore) { (address[] memory stakers, bool hasMoreStakers) = getStakers(rollup, startIndex, max); - address[] memory challenges = new address[](stakers.length); + uint64[] memory challenges = new uint64[](stakers.length); uint256 index = 0; + IChallengeManager challengeManager = rollup.challengeManager(); for (uint256 i = 0; i < stakers.length; i++) { address staker = stakers[i]; - address challengeAddr = address(rollup.currentChallenge(staker)); - if (challengeAddr != address(0)) { - IChallenge challenge = IChallenge(challengeAddr); - IChallenge.ChallengeData memory challengeInfo = IChallenge(challengeAddr).challengeInfo(); + uint64 challengeIndex = rollup.currentChallenge(staker); + if (challengeIndex != 0) { + IChallengeManager.Challenge memory challengeInfo = challengeManager.challengeInfo(challengeIndex); uint256 timeSinceLastMove = block.timestamp - challengeInfo.lastMoveTimestamp; if ( - timeSinceLastMove > challenge.currentResponderTimeLeft() && + timeSinceLastMove > challengeManager.currentResponderTimeLeft(challengeIndex) && challengeInfo.asserter == staker ) { - challenges[index] = challengeAddr; + challenges[index] = challengeIndex; index++; } } diff --git a/solgen/src/rollup/ValidatorWallet.sol b/solgen/src/rollup/ValidatorWallet.sol index 9ac86de413..7768e36e96 100644 --- a/solgen/src/rollup/ValidatorWallet.sol +++ b/solgen/src/rollup/ValidatorWallet.sol @@ -19,7 +19,7 @@ pragma solidity ^0.8.0; import "./IRollupLogic.sol"; -import "../challenge/IChallenge.sol"; +import "../challenge/IChallengeManager.sol"; import "../libraries/DelegateCallAware.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; @@ -84,10 +84,10 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { } } - function timeoutChallenges(IChallenge[] calldata challenges) external onlyOwner { + function timeoutChallenges(IChallengeManager manager, uint64[] calldata challenges) external onlyOwner { uint256 challengesCount = challenges.length; for (uint256 i = 0; i < challengesCount; i++) { - try challenges[i].timeout() {} catch (bytes memory error) { + try manager.timeout(challenges[i]) {} catch (bytes memory error) { if (error.length == 0) { // Assume out of gas // We need to revert here so gas estimation works diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index a2ffee0d77..69af5d99ff 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -205,7 +205,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) if faultyStaker { challengeAddr, err := rollup.CurrentChallenge(&bind.CallOpts{}, valWalletAddrA) Require(t, err) - if challengeAddr != (common.Address{}) { + if challengeAddr != 0 { atomic.StoreInt32(&stopBackgroundTxs, 1) } } diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index b3703f1059..c4345f8244 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -62,8 +62,9 @@ func (s GoGlobalState) AsSolidityStruct() challengegen.GlobalState { } type BlockChallengeBackend struct { - challengeCon *challengegen.Challenge + challengeCon *challengegen.ChallengeManager client bind.ContractBackend + challengeIndex uint64 bc *core.BlockChain startBlock int64 startPosition uint64 @@ -78,14 +79,22 @@ type BlockChallengeBackend struct { // Assert that BlockChallengeBackend implements ChallengeBackend var _ ChallengeBackend = (*BlockChallengeBackend)(nil) -func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNumber uint64) (*BlockChallengeBackend, error) { +func NewBlockChallengeBackend( + ctx context.Context, + challengeIndex uint64, + bc *core.BlockChain, + inboxTracker InboxTrackerInterface, + client bind.ContractBackend, + challengeAddr common.Address, + genesisBlockNumber uint64, +) (*BlockChallengeBackend, error) { callOpts := &bind.CallOpts{Context: ctx} - challengeCon, err := challengegen.NewChallenge(challengeAddr, client) + challengeCon, err := challengegen.NewChallengeManager(challengeAddr, client) if err != nil { return nil, err } - solStartGs, err := challengeCon.GetStartGlobalState(callOpts) + solStartGs, err := challengeCon.GetStartGlobalState(callOpts, challengeIndex) if err != nil { return nil, err } @@ -112,7 +121,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return nil, fmt.Errorf("start block %v and start message count %v don't correspond", startBlockNum, startMsgCount) } - solEndGs, err := challengeCon.GetEndGlobalState(callOpts) + solEndGs, err := challengeCon.GetEndGlobalState(callOpts, challengeIndex) if err != nil { return nil, err } @@ -128,6 +137,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra return &BlockChallengeBackend{ client: client, + challengeIndex: challengeIndex, challengeCon: challengeCon, bc: bc, startBlock: startBlockNum, @@ -141,7 +151,7 @@ func NewBlockChallengeBackend(ctx context.Context, bc *core.BlockChain, inboxTra }, nil } -func (b *BlockChallengeBackend) findBatchFromMessageCount(ctx context.Context, msgCount arbutil.MessageIndex) (uint64, error) { +func (b *BlockChallengeBackend) findBatchFromMessageCount(msgCount arbutil.MessageIndex) (uint64, error) { if msgCount == 0 { return 0, nil } @@ -174,12 +184,12 @@ func (b *BlockChallengeBackend) findBatchFromMessageCount(ctx context.Context, m } } -func (b *BlockChallengeBackend) FindGlobalStateFromHeader(ctx context.Context, header *types.Header) (GoGlobalState, error) { +func (b *BlockChallengeBackend) FindGlobalStateFromHeader(header *types.Header) (GoGlobalState, error) { if header == nil { return GoGlobalState{}, nil } msgCount := arbutil.BlockNumberToMessageCount(header.Number.Uint64(), b.genesisBlockNumber) - batch, err := b.findBatchFromMessageCount(ctx, msgCount) + batch, err := b.findBatchFromMessageCount(msgCount) if err != nil { return GoGlobalState{}, err } @@ -207,7 +217,7 @@ func (b *BlockChallengeBackend) GetBlockNrAtStep(step uint64) int64 { return b.startBlock + int64(step) } -func (b *BlockChallengeBackend) GetInfoAtStep(ctx context.Context, step uint64) (GoGlobalState, uint8, error) { +func (b *BlockChallengeBackend) GetInfoAtStep(step uint64) (GoGlobalState, uint8, error) { if step >= b.tooFarStartsAtPosition { return GoGlobalState{}, STATUS_TOO_FAR, nil } @@ -219,7 +229,7 @@ func (b *BlockChallengeBackend) GetInfoAtStep(ctx context.Context, step uint64) return GoGlobalState{}, 0, fmt.Errorf("failed to get block %v in block challenge", blockNum) } } - globalState, err := b.FindGlobalStateFromHeader(ctx, header) + globalState, err := b.FindGlobalStateFromHeader(header) if err != nil { return GoGlobalState{}, 0, err } @@ -230,11 +240,11 @@ func (b *BlockChallengeBackend) SetRange(ctx context.Context, start uint64, end if b.startPosition == start && b.endPosition == end { return nil } - newStartGs, _, err := b.GetInfoAtStep(ctx, start) + newStartGs, _, err := b.GetInfoAtStep(start) if err != nil { return err } - newEndGs, endStatus, err := b.GetInfoAtStep(ctx, end) + newEndGs, endStatus, err := b.GetInfoAtStep(end) if err != nil { return err } @@ -245,8 +255,8 @@ func (b *BlockChallengeBackend) SetRange(ctx context.Context, start uint64, end return nil } -func (b *BlockChallengeBackend) GetHashAtStep(ctx context.Context, position uint64) (common.Hash, error) { - gs, status, err := b.GetInfoAtStep(ctx, position) +func (b *BlockChallengeBackend) GetHashAtStep(_ context.Context, position uint64) (common.Hash, error) { + gs, status, err := b.GetInfoAtStep(position) if err != nil { return common.Hash{}, err } @@ -261,19 +271,21 @@ func (b *BlockChallengeBackend) GetHashAtStep(ctx context.Context, position uint } } -func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client bind.ContractBackend, auth *bind.TransactOpts, challenge common.Address, oldState *ChallengeState, startSegment int, numsteps uint64) (*types.Transaction, error) { - con, err := challengegen.NewChallenge(challenge, client) - if err != nil { - return nil, err - } +func (b *BlockChallengeBackend) IssueExecChallenge( + auth *bind.TransactOpts, + oldState *ChallengeState, + startSegment int, + numsteps uint64, +) (*types.Transaction, error) { position := oldState.Segments[startSegment].Position machineStatuses := [2]uint8{} globalStates := [2]GoGlobalState{} - globalStates[0], machineStatuses[0], err = b.GetInfoAtStep(ctx, position) + var err error + globalStates[0], machineStatuses[0], err = b.GetInfoAtStep(position) if err != nil { return nil, err } - globalStates[1], machineStatuses[1], err = b.GetInfoAtStep(ctx, position+1) + globalStates[1], machineStatuses[1], err = b.GetInfoAtStep(position + 1) if err != nil { return nil, err } @@ -281,9 +293,10 @@ func (b *BlockChallengeBackend) IssueExecChallenge(ctx context.Context, client b globalStates[0].Hash(), globalStates[1].Hash(), } - return con.ChallengeExecution( + return b.challengeCon.ChallengeExecution( auth, - challengegen.IChallengeSegmentSelection{ + b.challengeIndex, + challengegen.ChallengeLibSegmentSelection{ OldSegmentsStart: oldState.Start, OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), OldSegments: oldState.RawSegments, diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 31fdc31733..55d2e9237d 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -6,6 +6,7 @@ package validator import ( "context" + "encoding/binary" "fmt" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -28,12 +29,12 @@ var challengeBisectedID common.Hash var executionChallengeBegunID common.Hash func init() { - parsedChallengeCoreABI, err := challengegen.ChallengeMetaData.GetAbi() + parsedChallengeManagerABI, err := challengegen.ChallengeManagerMetaData.GetAbi() if err != nil { panic(err) } - challengeBisectedID = parsedChallengeCoreABI.Events["Bisected"].ID - executionChallengeBegunID = parsedChallengeCoreABI.Events["ExecutionChallengeBegun"].ID + challengeBisectedID = parsedChallengeManagerABI.Events["Bisected"].ID + executionChallengeBegunID = parsedChallengeManagerABI.Events["ExecutionChallengeBegun"].ID } type ChallengeBackend interface { @@ -43,13 +44,14 @@ type ChallengeBackend interface { type ChallengeManager struct { // fields used in both block and execution challenge - con *challengegen.Challenge - challengeAddr common.Address - client bind.ContractBackend - auth *bind.TransactOpts - actingAs common.Address - startL1Block *big.Int - confirmationBlocks int64 + con *challengegen.ChallengeManager + challengeManagerAddr common.Address + challengeIndex uint64 + client bind.ContractBackend + auth *bind.TransactOpts + actingAs common.Address + startL1Block *big.Int + confirmationBlocks int64 // fields below are used while working on block challenge blockChallengeBackend *BlockChallengeBackend @@ -68,8 +70,22 @@ type ChallengeManager struct { executionChallengeBackend *ExecutionChallengeBackend } -func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, fromAddr common.Address, blockChallengeAddr common.Address, l2blockChain *core.BlockChain, inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, startL1Block uint64, targetNumMachines int, confirmationBlocks int64) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewChallenge(blockChallengeAddr, l1client) +func NewChallengeManager( + ctx context.Context, + l1client bind.ContractBackend, + auth *bind.TransactOpts, + fromAddr common.Address, + challengeManagerAddr common.Address, + challengeIndex uint64, + l2blockChain *core.BlockChain, + inboxReader InboxReaderInterface, + inboxTracker InboxTrackerInterface, + txStreamer TransactionStreamerInterface, + startL1Block uint64, + targetNumMachines int, + confirmationBlocks int64, +) (*ChallengeManager, error) { + challengeCoreCon, err := challengegen.NewChallengeManager(challengeManagerAddr, l1client) if err != nil { return nil, err } @@ -77,13 +93,22 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut if err != nil { return nil, err } - backend, err := NewBlockChallengeBackend(ctx, l2blockChain, inboxTracker, l1client, blockChallengeAddr, genesisBlockNum) + backend, err := NewBlockChallengeBackend( + ctx, + challengeIndex, + l2blockChain, + inboxTracker, + l1client, + challengeManagerAddr, + genesisBlockNum, + ) if err != nil { return nil, err } return &ChallengeManager{ con: challengeCoreCon, - challengeAddr: blockChallengeAddr, + challengeManagerAddr: challengeManagerAddr, + challengeIndex: challengeIndex, client: l1client, auth: auth, actingAs: fromAddr, @@ -99,8 +124,17 @@ func NewChallengeManager(ctx context.Context, l1client bind.ContractBackend, aut } // for testing only - skips block challenges -func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, execChallengeAddr common.Address, initialMachine MachineInterface, startL1Block uint64, targetNumMachines int) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewChallenge(execChallengeAddr, l1client) +func NewExecutionChallengeManager( + ctx context.Context, + l1client bind.ContractBackend, + auth *bind.TransactOpts, + challengeManagerAddr common.Address, + challengeIndex uint64, + initialMachine MachineInterface, + startL1Block uint64, + targetNumMachines int, +) (*ChallengeManager, error) { + con, err := challengegen.NewChallengeManager(challengeManagerAddr, l1client) if err != nil { return nil, err } @@ -109,8 +143,9 @@ func NewExecutionChallengeManager(ctx context.Context, l1client bind.ContractBac return nil, err } return &ChallengeManager{ - con: challengeCoreCon, - challengeAddr: execChallengeAddr, + con: con, + challengeManagerAddr: challengeManagerAddr, + challengeIndex: challengeIndex, client: l1client, auth: auth, actingAs: auth.From, @@ -148,16 +183,22 @@ func (m *ChallengeManager) latestConfirmedBlock(ctx context.Context) (*big.Int, return block, nil } -func (m *ChallengeManager) ChallengeAddress() common.Address { - return m.challengeAddr +func (m *ChallengeManager) ChallengeIndex() uint64 { + return m.challengeIndex +} + +func (m *ChallengeManager) challengeFilterIndex() common.Hash { + var challengeIndex common.Hash + binary.BigEndian.PutUint64(challengeIndex[(32-8):], m.challengeIndex) + return challengeIndex } // Given the challenge's state hash, resolve the full challenge state via the Bisected event. func (m *ChallengeManager) resolveStateHash(ctx context.Context, stateHash common.Hash) (ChallengeState, error) { logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ FromBlock: m.startL1Block, - Addresses: []common.Address{m.challengeAddr}, - Topics: [][]common.Hash{{challengeBisectedID}, {stateHash}}, + Addresses: []common.Address{m.challengeManagerAddr}, + Topics: [][]common.Hash{{challengeBisectedID}, {m.challengeFilterIndex()}, {stateHash}}, }) if err != nil { return ChallengeState{}, err @@ -231,7 +272,8 @@ func (m *ChallengeManager) bisect(ctx context.Context, backend ChallengeBackend, } return m.con.BisectExecution( m.auth, - challengegen.IChallengeSegmentSelection{ + m.challengeIndex, + challengegen.ChallengeLibSegmentSelection{ OldSegmentsStart: oldState.Start, OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), OldSegments: oldState.RawSegments, @@ -243,7 +285,7 @@ func (m *ChallengeManager) bisect(ctx context.Context, backend ChallengeBackend, func (m *ChallengeManager) IsMyTurn(ctx context.Context) (bool, error) { callOpts := &bind.CallOpts{Context: ctx} - responder, err := m.con.CurrentResponder(callOpts) + responder, err := m.con.CurrentResponder(callOpts, m.challengeIndex) if err != nil { return false, err } @@ -255,7 +297,7 @@ func (m *ChallengeManager) IsMyTurn(ctx context.Context) (bool, error) { if err != nil { return false, err } - responder, err = m.con.CurrentResponder(callOpts) + responder, err = m.con.CurrentResponder(callOpts, m.challengeIndex) if err != nil { return false, err } @@ -272,7 +314,7 @@ func (m *ChallengeManager) GetChallengeState(ctx context.Context) (*ChallengeSta if err != nil { return nil, err } - challengeState, err := m.con.Challenge(callOpts) + challengeState, err := m.con.ChallengeInfo(callOpts, m.challengeIndex) if err != nil { return nil, errors.WithStack(err) } @@ -292,7 +334,7 @@ func (m *ChallengeManager) ScanChallengeState(ctx context.Context, backend Chall if err != nil { return 0, err } - log.Debug("checking challenge segment", "challenge", m.challengeAddr, "position", segment.Position, "ourHash", ourHash, "segmentHash", segment.Hash) + log.Debug("checking challenge segment", "challenge", m.challengeIndex, "position", segment.Position, "ourHash", ourHash, "segmentHash", segment.Hash) if segment.Hash != ourHash { if i == 0 { return 0, errors.Errorf( @@ -322,7 +364,7 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in return fmt.Errorf("block header %v before challenge point unknown", blockNum) } } - startGlobalState, err := m.blockChallengeBackend.FindGlobalStateFromHeader(ctx, blockHeader) + startGlobalState, err := m.blockChallengeBackend.FindGlobalStateFromHeader(blockHeader) if err != nil { return err } @@ -387,14 +429,14 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { Context: ctx, BlockNumber: latestConfirmedBlock, } - challengeState, err := m.con.Challenge(callOpts) + challengeState, err := m.con.ChallengeInfo(callOpts, m.challengeIndex) if err != nil || challengeState.Mode != challengeModeExecution { return errors.WithStack(err) } logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ FromBlock: m.startL1Block, - Addresses: []common.Address{m.challengeAddr}, - Topics: [][]common.Hash{{executionChallengeBegunID}}, + Addresses: []common.Address{m.challengeManagerAddr}, + Topics: [][]common.Hash{{executionChallengeBegunID}, {m.challengeFilterIndex()}}, }) if err != nil { return errors.WithStack(err) @@ -455,12 +497,20 @@ func (m *ChallengeManager) Act(ctx context.Context) (*types.Transaction, error) startPosition := state.Segments[nextMovePos].Position endPosition := state.Segments[nextMovePos+1].Position if startPosition+1 != endPosition { - log.Info("bisecting execution", "challenge", m.challengeAddr, "startPosition", startPosition, "endPosition", endPosition) + log.Info("bisecting execution", "challenge", m.challengeIndex, "startPosition", startPosition, "endPosition", endPosition) return m.bisect(ctx, backend, state, nextMovePos) } if m.executionChallengeBackend != nil { - log.Info("sending onestepproof", "challenge", m.challengeAddr, "startPosition", startPosition, "endPosition", endPosition) - return m.executionChallengeBackend.IssueOneStepProof(ctx, m.client, m.auth, m.challengeAddr, state, nextMovePos) + log.Info("sending onestepproof", "challenge", m.challengeIndex, "startPosition", startPosition, "endPosition", endPosition) + return m.executionChallengeBackend.IssueOneStepProof( + ctx, + m.client, + m.auth, + m.challengeIndex, + m.challengeManagerAddr, + state, + nextMovePos, + ) } blockNum := m.blockChallengeBackend.GetBlockNrAtStep(uint64(nextMovePos)) err = m.createInitialMachine(ctx, blockNum) @@ -482,6 +532,11 @@ func (m *ChallengeManager) Act(ctx context.Context) (*types.Transaction, error) stepCount += stepsPerLoop } stepCount = stepCountMachine.GetStepCount() - log.Info("issuing one step proof", "challenge", m.challengeAddr, "stepCount", stepCount, "blockNum", blockNum) - return m.blockChallengeBackend.IssueExecChallenge(ctx, m.client, m.auth, m.challengeAddr, state, nextMovePos, stepCount) + log.Info("issuing one step proof", "challenge", m.challengeIndex, "stepCount", stepCount, "blockNum", blockNum) + return m.blockChallengeBackend.IssueExecChallenge( + m.auth, + state, + nextMovePos, + stepCount, + ) } diff --git a/validator/challenge_test.go b/validator/challenge_test.go index 070f3d8b39..392806e0a3 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -127,7 +127,7 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step endMachineHash = IncorrectMachineHash(endMachineHash) } - resultReceiver, challenge := CreateChallenge( + resultReceiver, challengeManager := CreateChallenge( t, deployer, backend, @@ -149,10 +149,28 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step expectedWinner = asserter.From } - asserterManager, err := NewExecutionChallengeManager(ctx, backend, asserter, challenge, asserterMachine, 0, 4) + asserterManager, err := NewExecutionChallengeManager( + ctx, + backend, + asserter, + challengeManager, + 1, + asserterMachine, + 0, + 4, + ) Require(t, err) - challengerManager, err := NewExecutionChallengeManager(ctx, backend, challenger, challenge, challengerMachine, 0, 4) + challengerManager, err := NewExecutionChallengeManager( + ctx, + backend, + challenger, + challengeManager, + 1, + challengerMachine, + 0, + 4, + ) Require(t, err) for i := 0; i < 100; i++ { diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index 2b43aeb3a7..6a97e2f376 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -83,8 +83,16 @@ func (b *ExecutionChallengeBackend) GetHashAtStep(ctx context.Context, position return mach.Hash(), nil } -func (b *ExecutionChallengeBackend) IssueOneStepProof(ctx context.Context, client bind.ContractBackend, auth *bind.TransactOpts, challenge common.Address, oldState *ChallengeState, startSegment int) (*types.Transaction, error) { - con, err := challengegen.NewChallenge(challenge, client) +func (b *ExecutionChallengeBackend) IssueOneStepProof( + ctx context.Context, + client bind.ContractBackend, + auth *bind.TransactOpts, + challengeIndex uint64, + challengeManager common.Address, + oldState *ChallengeState, + startSegment int, +) (*types.Transaction, error) { + con, err := challengegen.NewChallengeManager(challengeManager, client) if err != nil { return nil, err } @@ -95,7 +103,8 @@ func (b *ExecutionChallengeBackend) IssueOneStepProof(ctx context.Context, clien proof := mach.ProveNextStep() return con.OneStepProveExecution( auth, - challengegen.IChallengeSegmentSelection{ + challengeIndex, + challengegen.ChallengeLibSegmentSelection{ OldSegmentsStart: oldState.Start, OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), OldSegments: oldState.RawSegments, diff --git a/validator/l1_validator.go b/validator/l1_validator.go index de2a0b7fa3..e213c324a1 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -23,18 +23,19 @@ import ( ) type Validator struct { - rollup *RollupWatcher - rollupAddress common.Address - sequencerInbox *bridgegen.SequencerInbox - validatorUtils *rollupgen.ValidatorUtils - client arbutil.L1Interface - builder *BuilderBackend - wallet *ValidatorWallet - callOpts bind.CallOpts - GasThreshold *big.Int - SendThreshold *big.Int - BlockThreshold *big.Int - genesisBlockNumber uint64 + rollup *RollupWatcher + rollupAddress common.Address + challengeManagerAddress common.Address + sequencerInbox *bridgegen.SequencerInbox + validatorUtils *rollupgen.ValidatorUtils + client arbutil.L1Interface + builder *BuilderBackend + wallet *ValidatorWallet + callOpts bind.CallOpts + GasThreshold *big.Int + SendThreshold *big.Int + BlockThreshold *big.Int + genesisBlockNumber uint64 l2Blockchain *core.BlockChain inboxReader InboxReaderInterface @@ -69,6 +70,10 @@ func NewValidator( if err != nil { return nil, err } + challengeManagerAddress, err := rollup.ChallengeManager(&localCallOpts) + if err != nil { + return nil, err + } sequencerInbox, err := bridgegen.NewSequencerInbox(sequencerBridgeAddress, client) if err != nil { return nil, err @@ -85,23 +90,24 @@ func NewValidator( return nil, err } return &Validator{ - rollup: rollup, - rollupAddress: wallet.RollupAddress(), - sequencerInbox: sequencerInbox, - validatorUtils: validatorUtils, - client: client, - builder: builder, - wallet: wallet, - GasThreshold: big.NewInt(100_000_000_000), - SendThreshold: big.NewInt(5), - BlockThreshold: big.NewInt(960), - callOpts: callOpts, - genesisBlockNumber: genesisBlockNumber, - l2Blockchain: l2Blockchain, - inboxReader: inboxReader, - inboxTracker: inboxTracker, - txStreamer: txStreamer, - blockValidator: blockValidator, + rollup: rollup, + rollupAddress: wallet.RollupAddress(), + challengeManagerAddress: challengeManagerAddress, + sequencerInbox: sequencerInbox, + validatorUtils: validatorUtils, + client: client, + builder: builder, + wallet: wallet, + GasThreshold: big.NewInt(100_000_000_000), + SendThreshold: big.NewInt(5), + BlockThreshold: big.NewInt(960), + callOpts: callOpts, + genesisBlockNumber: genesisBlockNumber, + l2Blockchain: l2Blockchain, + inboxReader: inboxReader, + inboxTracker: inboxTracker, + txStreamer: txStreamer, + blockValidator: blockValidator, }, nil } @@ -145,7 +151,7 @@ func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*types.Trans return nil, nil } log.Info("timing out challenges", "count", len(challengesToEliminate)) - return v.wallet.TimeoutChallenges(ctx, challengesToEliminate) + return v.wallet.TimeoutChallenges(ctx, v.challengeManagerAddress, challengesToEliminate) } func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo) error { diff --git a/validator/rollup_watcher.go b/validator/rollup_watcher.go index d24ba91874..6a10e00e7b 100644 --- a/validator/rollup_watcher.go +++ b/validator/rollup_watcher.go @@ -36,7 +36,7 @@ type StakerInfo struct { Index uint64 LatestStakedNode uint64 AmountStaked *big.Int - CurrentChallenge *common.Address + CurrentChallenge *uint64 } type RollupWatcher struct { @@ -252,8 +252,7 @@ func (r *RollupWatcher) StakerInfo(ctx context.Context, staker common.Address) ( LatestStakedNode: info.LatestStakedNode, AmountStaked: info.AmountStaked, } - emptyAddress := common.Address{} - if info.CurrentChallenge != emptyAddress { + if info.CurrentChallenge != 0 { chal := info.CurrentChallenge stakerInfo.CurrentChallenge = &chal } diff --git a/validator/staker.go b/validator/staker.go index 528a038a38..2c7e52a68c 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -364,7 +364,7 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { return nil } - if s.activeChallenge == nil || s.activeChallenge.ChallengeAddress() != *info.CurrentChallenge { + if s.activeChallenge == nil || s.activeChallenge.ChallengeIndex() != *info.CurrentChallenge { log.Warn("entered challenge", "challenge", info.CurrentChallenge) latestConfirmedCreated, err := s.rollup.LatestConfirmedCreationBlock(ctx) @@ -372,7 +372,21 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { return err } - newChallengeManager, err := NewChallengeManager(ctx, s.builder, s.builder.builderAuth, *s.builder.wallet.Address(), *info.CurrentChallenge, s.l2Blockchain, s.inboxReader, s.inboxTracker, s.txStreamer, latestConfirmedCreated, s.config.TargetNumMachines, s.config.ConfirmationBlocks) + newChallengeManager, err := NewChallengeManager( + ctx, + s.builder, + s.builder.builderAuth, + *s.builder.wallet.Address(), + s.challengeManagerAddress, + *info.CurrentChallenge, + s.l2Blockchain, + s.inboxReader, + s.inboxTracker, + s.txStreamer, + latestConfirmedCreated, + s.config.TargetNumMachines, + s.config.ConfirmationBlocks, + ) if err != nil { return err } diff --git a/validator/validator_utils.go b/validator/validator_utils.go index 6b5663fc5f..53ed7a2cd6 100644 --- a/validator/validator_utils.go +++ b/validator/validator_utils.go @@ -71,12 +71,12 @@ func (v *ValidatorUtils) RefundableStakers(ctx context.Context) ([]common.Addres return addresses, nil } -func (v *ValidatorUtils) TimedOutChallenges(ctx context.Context, max int) ([]common.Address, error) { +func (v *ValidatorUtils) TimedOutChallenges(ctx context.Context, max int) ([]uint64, error) { var count uint64 = 1024 - addresses := make([]common.Address, 0) + addresses := make([]uint64, 0) for i := uint64(0); ; i += count { - newAddrs, hasMore, err := v.con.TimedOutChallenges(v.getCallOpts(ctx), v.rollupAddress, i, count) - addresses = append(addresses, newAddrs...) + newIndexes, hasMore, err := v.con.TimedOutChallenges(v.getCallOpts(ctx), v.rollupAddress, i, count) + addresses = append(addresses, newIndexes...) if err != nil { return nil, errors.WithStack(err) } diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go index 1a8c9aabf5..8b3513aa55 100644 --- a/validator/validator_wallet.go +++ b/validator/validator_wallet.go @@ -175,8 +175,8 @@ func (v *ValidatorWallet) ReturnOldDeposits(ctx context.Context, stakers []commo return v.con.ReturnOldDeposits(v.auth, v.rollupAddress, stakers) } -func (v *ValidatorWallet) TimeoutChallenges(ctx context.Context, challenges []common.Address) (*types.Transaction, error) { - return v.con.TimeoutChallenges(v.auth, challenges) +func (v *ValidatorWallet) TimeoutChallenges(ctx context.Context, manager common.Address, challenges []uint64) (*types.Transaction, error) { + return v.con.TimeoutChallenges(v.auth, manager, challenges) } func CreateValidatorWallet( From da99d89ceed619e6d97cbe0390eef4ad611d6aca Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 03:23:54 -0500 Subject: [PATCH 048/110] Linting fixes --- system_tests/full_challenge_test.go | 4 ++-- validator/block_challenge_backend.go | 16 ++++++++-------- validator/challenge_manager.go | 3 +-- validator/challenge_test.go | 2 -- validator/execution_challenge_backend.go | 6 +++++- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index e07c14829f..fc0021b342 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -99,8 +99,8 @@ func CreateChallenge( }, wasmModuleRoot, [2]uint8{ - validator.STATUS_FINISHED, - validator.STATUS_FINISHED, + validator.StatusFinished, + validator.StatusFinished, }, [2]challengegen.GlobalState{ startGlobalState.AsSolidityStruct(), diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index c4345f8244..3e2a10cc53 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -210,8 +210,8 @@ func (b *BlockChallengeBackend) FindGlobalStateFromHeader(header *types.Header) return GoGlobalState{header.Hash(), extraInfo.SendRoot, batch, uint64(msgCount - batchMsgCount)}, nil } -const STATUS_FINISHED uint8 = 1 -const STATUS_TOO_FAR uint8 = 3 +const StatusFinished uint8 = 1 +const StatusTooFar uint8 = 3 func (b *BlockChallengeBackend) GetBlockNrAtStep(step uint64) int64 { return b.startBlock + int64(step) @@ -219,7 +219,7 @@ func (b *BlockChallengeBackend) GetBlockNrAtStep(step uint64) int64 { func (b *BlockChallengeBackend) GetInfoAtStep(step uint64) (GoGlobalState, uint8, error) { if step >= b.tooFarStartsAtPosition { - return GoGlobalState{}, STATUS_TOO_FAR, nil + return GoGlobalState{}, StatusTooFar, nil } blockNum := b.GetBlockNrAtStep(step) var header *types.Header @@ -233,10 +233,10 @@ func (b *BlockChallengeBackend) GetInfoAtStep(step uint64) (GoGlobalState, uint8 if err != nil { return GoGlobalState{}, 0, err } - return globalState, STATUS_FINISHED, nil + return globalState, StatusFinished, nil } -func (b *BlockChallengeBackend) SetRange(ctx context.Context, start uint64, end uint64) error { +func (b *BlockChallengeBackend) SetRange(_ context.Context, start uint64, end uint64) error { if b.startPosition == start && b.endPosition == end { return nil } @@ -249,7 +249,7 @@ func (b *BlockChallengeBackend) SetRange(ctx context.Context, start uint64, end return err } b.startGs = newStartGs - if endStatus == STATUS_FINISHED { + if endStatus == StatusFinished { b.endGs = newEndGs } return nil @@ -260,11 +260,11 @@ func (b *BlockChallengeBackend) GetHashAtStep(_ context.Context, position uint64 if err != nil { return common.Hash{}, err } - if status == STATUS_FINISHED { + if status == StatusFinished { data := []byte("Block state:") data = append(data, gs.Hash().Bytes()...) return crypto.Keccak256Hash(data), nil - } else if status == STATUS_TOO_FAR { + } else if status == StatusTooFar { return crypto.Keccak256Hash([]byte("Block state, too far:")), nil } else { panic(fmt.Sprintf("Unknown block status: %v", status)) diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 55d2e9237d..dea8ae533c 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -123,9 +123,8 @@ func NewChallengeManager( }, nil } -// for testing only - skips block challenges +// NewExecutionChallengeManager is for testing only - skips block challenges func NewExecutionChallengeManager( - ctx context.Context, l1client bind.ContractBackend, auth *bind.TransactOpts, challengeManagerAddr common.Address, diff --git a/validator/challenge_test.go b/validator/challenge_test.go index 392806e0a3..a05eb56f33 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -150,7 +150,6 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step } asserterManager, err := NewExecutionChallengeManager( - ctx, backend, asserter, challengeManager, @@ -162,7 +161,6 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step Require(t, err) challengerManager, err := NewExecutionChallengeManager( - ctx, backend, challenger, challengeManager, diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index 6a97e2f376..b1051089dc 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -28,7 +28,11 @@ type ExecutionChallengeBackend struct { var _ ChallengeBackend = (*ExecutionChallengeBackend)(nil) // machineCache may be nil, but if present, it must not have a restricted range -func NewExecutionChallengeBackend(initialMachine MachineInterface, targetNumMachines int, machineCache *MachineCache) (*ExecutionChallengeBackend, error) { +func NewExecutionChallengeBackend( + initialMachine MachineInterface, + targetNumMachines int, + machineCache *MachineCache, +) (*ExecutionChallengeBackend, error) { if initialMachine.GetStepCount() != 0 { return nil, errors.New("initialMachine not at step count 0") } From fe7ce5f9edb68d10219149823dc74e16eb8d75dd Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 03:26:31 -0500 Subject: [PATCH 049/110] Only rollup create challenges --- solgen/src/challenge/ChallengeManager.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 35f5dca4c7..e99028a450 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -15,7 +15,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { string constant NO_TURN = "NO_TURN"; uint256 constant MAX_CHALLENGE_DEGREE = 40; - uint64 totalChallengesCreated; + uint64 public totalChallengesCreated; mapping (uint256 => Challenge) public challenges; IChallengeResultReceiver public resultReceiver; @@ -72,6 +72,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint256 asserterTimeLeft_, uint256 challengerTimeLeft_ ) external override returns (uint64) { + require(msg.sender == address(resultReceiver), "ONLY_ROLLUP_CHAL"); bytes32[] memory segments = new bytes32[](2); segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); segments[1] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[1], startAndEndGlobalStates_[1].hash()); From 5b8bcb93a0b000f444c5ab2e41f19e2e979d7105 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 11:56:30 -0500 Subject: [PATCH 050/110] Add NO_CHAL_INDEX constant --- solgen/src/challenge/ChallengeManager.sol | 4 ++++ solgen/src/libraries/Constants.sol | 2 ++ solgen/src/rollup/RollupAdminLogic.sol | 4 +++- solgen/src/rollup/RollupCore.sol | 4 +++- solgen/src/rollup/ValidatorUtils.sol | 4 +++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index e99028a450..09a1a5d37c 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -8,6 +8,8 @@ import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; import "./IChallengeManager.sol"; +import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; + contract ChallengeManager is DelegateCallAware, IChallengeManager { using GlobalStateLib for GlobalState; using MachineLib for Machine; @@ -79,6 +81,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numBlocks, segments); uint64 challengeIndex = ++totalChallengesCreated; + // The following is an assertion since it should never be possible, but it's an important invariant + assert(challengeIndex != NO_CHAL_INDEX); Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; // No need to set maxInboxMessages until execution challenge diff --git a/solgen/src/libraries/Constants.sol b/solgen/src/libraries/Constants.sol index 1c52b531c9..200fc85068 100644 --- a/solgen/src/libraries/Constants.sol +++ b/solgen/src/libraries/Constants.sol @@ -20,3 +20,5 @@ pragma solidity ^0.8.4; // 90% of Geth's 128KB tx size limit, leaving ~13KB for proving uint256 constant MAX_DATA_SIZE = 117964; + +uint256 constant NO_CHAL_INDEX = 0; \ No newline at end of file diff --git a/solgen/src/rollup/RollupAdminLogic.sol b/solgen/src/rollup/RollupAdminLogic.sol index 6bfec328f5..440870e7f9 100644 --- a/solgen/src/rollup/RollupAdminLogic.sol +++ b/solgen/src/rollup/RollupAdminLogic.sol @@ -10,6 +10,8 @@ import "../challenge/IChallengeManager.sol"; import "../libraries/SecondaryLogicUUPSUpgradeable.sol"; import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; +import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; + contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgradeable { function initialize( Config calldata config, @@ -263,7 +265,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade for (uint256 i = 0; i < stakerA.length; i++) { uint64 chall = inChallenge(stakerA[i], stakerB[i]); - require(chall != 0, "NOT_IN_CHALL"); + require(chall != NO_CHAL_INDEX, "NOT_IN_CHALL"); clearChallenge(stakerA[i]); clearChallenge(stakerB[i]); challengeManager.clearChallenge(chall); diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index c553cc7517..3db2871bec 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -32,6 +32,8 @@ import "../bridge/ISequencerInbox.sol"; import "../bridge/IBridge.sol"; import "../bridge/IOutbox.sol"; +import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; + abstract contract RollupCore is IRollupCore, PausableUpgradeable { using NodeLib for Node; using GlobalStateLib for GlobalState; @@ -345,7 +347,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { Staker storage staker1 = _stakerMap[stakerAddress1]; Staker storage staker2 = _stakerMap[stakerAddress2]; uint64 challenge = staker1.currentChallenge; - require(challenge != 0, "NO_CHAL"); + require(challenge != NO_CHAL_INDEX, "NO_CHAL"); require(challenge == staker2.currentChallenge, "DIFF_IN_CHAL"); return challenge; } diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index efe5a36d4b..5aeaa77bd8 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -24,6 +24,8 @@ import "../rollup/IRollupCore.sol"; import "../rollup/IRollupLogic.sol"; import "../challenge/IChallengeManager.sol"; +import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; + contract ValidatorUtils { using NodeLib for Node; @@ -246,7 +248,7 @@ contract ValidatorUtils { for (uint256 i = 0; i < stakers.length; i++) { address staker = stakers[i]; uint64 challengeIndex = rollup.currentChallenge(staker); - if (challengeIndex != 0) { + if (challengeIndex != NO_CHAL_INDEX) { IChallengeManager.Challenge memory challengeInfo = challengeManager.challengeInfo(challengeIndex); uint256 timeSinceLastMove = block.timestamp - challengeInfo.lastMoveTimestamp; if ( From ef2dc8acfb9802260a0be2d3cbd0f6a994ef5cc2 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 12:36:55 -0500 Subject: [PATCH 051/110] Simplify turn logic in challengeExecution --- solgen/src/challenge/ChallengeManager.sol | 34 ++++++----------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 09a1a5d37c..fe175fb88b 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -32,10 +32,10 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { modifier takeTurn(uint64 challengeIndex) { Challenge storage challenge = challenges[challengeIndex]; - require(msg.sender == currentResponder(challengeIndex), "BIS_SENDER"); + require(msg.sender == currentResponder(challengeIndex), "CHAL_SENDER"); require( block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(challengeIndex), - "BIS_DEADLINE" + "CHAL_DEADLINE" ); _; @@ -43,9 +43,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { if (challenge.turn == Turn.CHALLENGER) { challenge.challengerTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; challenge.turn = Turn.ASSERTER; - } else { + } else if (challenge.turn == Turn.ASSERTER) { challenge.asserterTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; challenge.turn = Turn.CHALLENGER; + } else { + revert(NO_TURN); } challenge.lastMoveTimestamp = block.timestamp; } @@ -168,14 +170,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { MachineStatus[2] calldata machineStatuses, bytes32[2] calldata globalStateHashes, uint256 numSteps - ) external { + ) external takeTurn(challengeIndex) { Challenge storage challenge = challenges[challengeIndex]; - require(msg.sender == currentResponder(challengeIndex), "EXEC_SENDER"); - require( - block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(challengeIndex), - "EXEC_DEADLINE" - ); - (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); require(challengeLength == 1, "TOO_LONG"); @@ -227,28 +223,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { maxInboxMessagesRead++; } - if (challenge.turn == Turn.CHALLENGER) { - (challenge.asserter, challenge.challenger) = (challenge.challenger, challenge.asserter); - ( - challenge.asserterTimeLeft, - challenge.challengerTimeLeft - ) = ( - challenge.challengerTimeLeft, - challenge.asserterTimeLeft - ); - } else if (challenge.turn != Turn.ASSERTER) { - revert(NO_TURN); - } - require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); - challenge.maxInboxMessages = challenge.maxInboxMessages; bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; segments[1] = startAndEndHashes[1]; bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps, segments); + + challenge.maxInboxMessages = challenge.maxInboxMessages; challenge.challengeStateHash = challengeStateHash; - challenge.lastMoveTimestamp = block.timestamp; - challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.EXECUTION; emit Bisected( From bc0473d11c7c88beea0b49bf537734945a3e7610 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 12:45:08 -0500 Subject: [PATCH 052/110] Refactor into completeBisection --- solgen/src/challenge/ChallengeManager.sol | 42 ++++++++++++++--------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index fe175fb88b..789f416b51 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -80,7 +80,6 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { bytes32[] memory segments = new bytes32[](2); segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); segments[1] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[1], startAndEndGlobalStates_[1].hash()); - bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numBlocks, segments); uint64 challengeIndex = ++totalChallengesCreated; // The following is an assertion since it should never be possible, but it's an important invariant @@ -97,12 +96,10 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { challenge.lastMoveTimestamp = block.timestamp; challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.BLOCK; - challenge.challengeStateHash = challengeStateHash; emit InitiatedChallenge(challengeIndex); - emit Bisected( + completeBisection( challengeIndex, - challengeStateHash, 0, numBlocks, segments @@ -148,16 +145,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require(selection.oldSegments[selection.challengePosition] == newSegments[0], "DIFF_START"); - bytes32 challengeStateHash = ChallengeLib.hashChallengeState( - challengeStart, - challengeLength, - newSegments - ); - challenge.challengeStateHash = challengeStateHash; - - emit Bisected( + completeBisection( challengeIndex, - challengeStateHash, challengeStart, challengeLength, newSegments @@ -227,15 +216,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; segments[1] = startAndEndHashes[1]; - bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps, segments); challenge.maxInboxMessages = challenge.maxInboxMessages; - challenge.challengeStateHash = challengeStateHash; challenge.mode = ChallengeMode.EXECUTION; - emit Bisected( + completeBisection( challengeIndex, - challengeStateHash, 0, numSteps, segments @@ -319,6 +305,28 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } } + function completeBisection( + uint64 challengeIndex, + uint256 challengeStart, + uint256 challengeLength, + bytes32[] memory newSegments + ) private { + bytes32 challengeStateHash = ChallengeLib.hashChallengeState( + challengeStart, + challengeLength, + newSegments + ); + challenges[challengeIndex].challengeStateHash = challengeStateHash; + + emit Bisected( + challengeIndex, + challengeStateHash, + challengeStart, + challengeLength, + newSegments + ); + } + function _asserterWin(Challenge storage challenge) private { challenge.turn = Turn.NO_CHALLENGE; resultReceiver.completeChallenge(challenge.asserter, challenge.challenger); From c0ba8b9e474b3bb1874863e051f9931d5956d0f2 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 13:47:59 -0500 Subject: [PATCH 053/110] Refactor more common code into takeTurn --- solgen/src/challenge/ChallengeLib.sol | 18 +------ solgen/src/challenge/ChallengeManager.sol | 65 +++++++++++++---------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 22b286d4c7..04b7eea777 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -71,22 +71,8 @@ library ChallengeLib { } } - function extractChallengeSegment(bytes32 currentStateHash, SegmentSelection calldata selection) internal pure returns (uint256 segmentStart, uint256 segmentLength) { - require( - currentStateHash == - ChallengeLib.hashChallengeState( - selection.oldSegmentsStart, - selection.oldSegmentsLength, - selection.oldSegments - ), - "BIS_STATE" - ); - if ( - selection.oldSegments.length < 2 || - selection.challengePosition >= selection.oldSegments.length - 1 - ) { - revert("BAD_CHALLENGE_POS"); - } + + function extractChallengeSegment(SegmentSelection calldata selection) internal pure returns (uint256 segmentStart, uint256 segmentLength) { uint256 oldChallengeDegree = selection.oldSegments.length - 1; segmentLength = selection.oldSegmentsLength / oldChallengeDegree; // Intentionally done before challengeLength is potentially added to for the final segment diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 789f416b51..7c74866651 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -30,7 +30,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { return challenges[challengeIndex]; } - modifier takeTurn(uint64 challengeIndex) { + modifier takeTurn(uint64 challengeIndex, ChallengeLib.SegmentSelection calldata selection) { Challenge storage challenge = challenges[challengeIndex]; require(msg.sender == currentResponder(challengeIndex), "CHAL_SENDER"); require( @@ -38,6 +38,22 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { "CHAL_DEADLINE" ); + require( + challenge.challengeStateHash == + ChallengeLib.hashChallengeState( + selection.oldSegmentsStart, + selection.oldSegmentsLength, + selection.oldSegments + ), + "BIS_STATE" + ); + if ( + selection.oldSegments.length < 2 || + selection.challengePosition >= selection.oldSegments.length - 1 + ) { + revert("BAD_CHALLENGE_POS"); + } + _; if (challenge.turn == Turn.CHALLENGER) { @@ -126,9 +142,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint64 challengeIndex, ChallengeLib.SegmentSelection calldata selection, bytes32[] calldata newSegments - ) external takeTurn(challengeIndex) { - Challenge storage challenge = challenges[challengeIndex]; - (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); + ) external takeTurn(challengeIndex, selection) { + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); require(challengeLength > 1, "TOO_SHORT"); { uint256 expectedDegree = challengeLength; @@ -159,28 +174,29 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { MachineStatus[2] calldata machineStatuses, bytes32[2] calldata globalStateHashes, uint256 numSteps - ) external takeTurn(challengeIndex) { - Challenge storage challenge = challenges[challengeIndex]; - (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); - require(challengeLength == 1, "TOO_LONG"); - + ) external takeTurn(challengeIndex, selection) { + require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); require( selection.oldSegments[selection.challengePosition] == - ChallengeLib.blockStateHash( - machineStatuses[0], - globalStateHashes[0] - ), + ChallengeLib.blockStateHash( + machineStatuses[0], + globalStateHashes[0] + ), "WRONG_START" ); require( selection.oldSegments[selection.challengePosition + 1] != - ChallengeLib.blockStateHash( - machineStatuses[1], - globalStateHashes[1] - ), + ChallengeLib.blockStateHash( + machineStatuses[1], + globalStateHashes[1] + ), "SAME_END" ); + Challenge storage challenge = challenges[challengeIndex]; + (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); + require(challengeLength == 1, "TOO_LONG"); + if (machineStatuses[0] != MachineStatus.FINISHED) { // If the machine is in a halted state, it can't change require( @@ -197,12 +213,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require(globalStateHashes[0] == globalStateHashes[1], "ERROR_CHANGE"); } - bytes32[2] memory startAndEndHashes; - startAndEndHashes[0] = ChallengeLib.getStartMachineHash( + bytes32[] memory segments = new bytes32[](2); + segments[0] = ChallengeLib.getStartMachineHash( globalStateHashes[0], challenge.wasmModuleRoot ); - startAndEndHashes[1] = ChallengeLib.getEndMachineHash( + segments[1] = ChallengeLib.getEndMachineHash( machineStatuses[1], globalStateHashes[1] ); @@ -212,11 +228,6 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { maxInboxMessagesRead++; } - require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); - bytes32[] memory segments = new bytes32[](2); - segments[0] = startAndEndHashes[0]; - segments[1] = startAndEndHashes[1]; - challenge.maxInboxMessages = challenge.maxInboxMessages; challenge.mode = ChallengeMode.EXECUTION; @@ -234,9 +245,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint64 challengeIndex, ChallengeLib.SegmentSelection calldata selection, bytes calldata proof - ) external takeTurn(challengeIndex) { + ) external takeTurn(challengeIndex, selection) { Challenge storage challenge = challenges[challengeIndex]; - (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(challenge.challengeStateHash, selection); + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); bytes32 afterHash = osp.proveOneStep( From c73839c7aae6b04a54814bbaebb5870d71b47e76 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 14:02:43 -0500 Subject: [PATCH 054/110] Refactor valid bisection check --- solgen/src/challenge/ChallengeManager.sol | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 7c74866651..9e3cfab87b 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -152,13 +152,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } require(newSegments.length == expectedDegree + 1, "WRONG_DEGREE"); } - require( - newSegments[newSegments.length - 1] != - selection.oldSegments[selection.challengePosition + 1], - "SAME_END" - ); - require(selection.oldSegments[selection.challengePosition] == newSegments[0], "DIFF_START"); + requireValidBisection( + selection, + newSegments[0], + newSegments[newSegments.length - 1] + ); completeBisection( challengeIndex, @@ -176,21 +175,16 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint256 numSteps ) external takeTurn(challengeIndex, selection) { require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); - require( - selection.oldSegments[selection.challengePosition] == + requireValidBisection( + selection, ChallengeLib.blockStateHash( machineStatuses[0], globalStateHashes[0] ), - "WRONG_START" - ); - require( - selection.oldSegments[selection.challengePosition + 1] != ChallengeLib.blockStateHash( machineStatuses[1], globalStateHashes[1] - ), - "SAME_END" + ) ); Challenge storage challenge = challenges[challengeIndex]; @@ -316,6 +310,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } } + function requireValidBisection( + ChallengeLib.SegmentSelection calldata selection, + bytes32 startHash, + bytes32 endHash + ) { + require(selection.oldSegments[selection.challengePosition] == startHash, "WRONG_START"); + require(selection.oldSegments[selection.challengePosition + 1] != endHash, "SAME_END"); + } + function completeBisection( uint64 challengeIndex, uint256 challengeStart, From e6980d054d4040e800b8b6c9e82faf9cdba6eddd Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 14:27:42 -0500 Subject: [PATCH 055/110] Refactor timeout and challenge completion code --- solgen/src/challenge/ChallengeManager.sol | 48 ++++++++++++---------- solgen/src/challenge/IChallengeManager.sol | 15 ++++++- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 9e3cfab87b..460e45a14a 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -33,10 +33,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { modifier takeTurn(uint64 challengeIndex, ChallengeLib.SegmentSelection calldata selection) { Challenge storage challenge = challenges[challengeIndex]; require(msg.sender == currentResponder(challengeIndex), "CHAL_SENDER"); - require( - block.timestamp - challenge.lastMoveTimestamp <= currentResponderTimeLeft(challengeIndex), - "CHAL_DEADLINE" - ); + require(!isTimedOut(challengeIndex), "CHAL_DEADLINE"); require( challenge.challengeStateHash == @@ -57,10 +54,10 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { _; if (challenge.turn == Turn.CHALLENGER) { - challenge.challengerTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; + challenge.challengerTimeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.ASSERTER; } else if (challenge.turn == Turn.ASSERTER) { - challenge.asserterTimeLeft -= block.timestamp - challenge.lastMoveTimestamp; + challenge.asserterTimeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.CHALLENGER; } else { revert(NO_TURN); @@ -265,27 +262,22 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function timeout(uint64 challengeIndex) external override { Challenge storage challenge = challenges[challengeIndex]; - uint256 timeSinceLastMove = block.timestamp - challenge.lastMoveTimestamp; - require( - timeSinceLastMove > currentResponderTimeLeft(challengeIndex), - "TIMEOUT_DEADLINE" - ); + require(isTimedOut(challengeIndex), "TIMEOUT_DEADLINE"); if (challenge.turn == Turn.ASSERTER) { - emit AsserterTimedOut(challengeIndex); - _challengerWin(challenge); + _challengerWin(challengeIndex, ChallengeTerminationType.TIMEOUT); } else if (challenge.turn == Turn.CHALLENGER) { - emit ChallengerTimedOut(challengeIndex); - _asserterWin(challenge); + _asserterWin(challengeIndex, ChallengeTerminationType.TIMEOUT); } else { revert(NO_TURN); } } + event ChallengeTerminated(uint64 indexed challengeIndex); function clearChallenge(uint64 challengeIndex) external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); - Challenge storage challenge = challenges[challengeIndex]; - challenge.turn = Turn.NO_CHALLENGE; + delete challenges[challengeIndex]; + emit ChallengeTerminated(challengeIndex); } function currentResponder(uint64 challengeIndex) public view returns (address) { @@ -310,11 +302,19 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } } + function isTimedOut(uint64 challengeIndex) public view returns (bool) { + return timeUsedSinceLastMove(challengeIndex) > currentResponderTimeLeft(challengeIndex); + } + + function timeUsedSinceLastMove(uint64 challengeIndex) private view returns (uint256) { + return block.timestamp - challenges[challengeIndex].lastMoveTimestamp; + } + function requireValidBisection( ChallengeLib.SegmentSelection calldata selection, bytes32 startHash, bytes32 endHash - ) { + ) private pure { require(selection.oldSegments[selection.challengePosition] == startHash, "WRONG_START"); require(selection.oldSegments[selection.challengePosition + 1] != endHash, "SAME_END"); } @@ -341,14 +341,18 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ); } - function _asserterWin(Challenge storage challenge) private { - challenge.turn = Turn.NO_CHALLENGE; + function _asserterWin(uint64 challengeIndex, ChallengeTerminationType reason) private { + Challenge storage challenge = challenges[challengeIndex]; resultReceiver.completeChallenge(challenge.asserter, challenge.challenger); + delete challenges[challengeIndex]; + emit ChallengedEnded(challengeIndex, ChallengeWinner.ASSERTER, reason); } - function _challengerWin(Challenge storage challenge) private { - challenge.turn = Turn.NO_CHALLENGE; + function _challengerWin(uint64 challengeIndex, ChallengeTerminationType reason) private { + Challenge storage challenge = challenges[challengeIndex]; resultReceiver.completeChallenge(challenge.challenger, challenge.asserter); + delete challenges[challengeIndex]; + emit ChallengedEnded(challengeIndex, ChallengeWinner.CHALLENGER, reason); } function _currentWin(Challenge storage challenge) private { diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index 0033c4e28f..50aca04315 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -22,6 +22,17 @@ interface IChallengeManager { EXECUTION } + enum ChallengeWinner { + NONE, + ASSERTER, + CHALLENGER + } + + enum ChallengeTerminationType { + TIMEOUT, + CHALLENGER_TIMED_OUT + } + struct Challenge { address asserter; address challenger; @@ -49,12 +60,12 @@ interface IChallengeManager { uint256 challengedSegmentLength, bytes32[] chainHashes ); - event AsserterTimedOut(uint64 indexed challengeIndex); - event ChallengerTimedOut(uint64 indexed challengeIndex); event ExecutionChallengeBegun(uint64 indexed challengeIndex, uint256 blockSteps); event OneStepProofCompleted(uint64 indexed challengeIndex); + event ChallengedEnded(uint64 indexed challengeIndex, ChallengeWinner winner, ChallengeTerminationType kind); + function initialize( IChallengeResultReceiver resultReceiver_, ISequencerInbox sequencerInbox_, From 25e49078bab01c2a460f6220b857fa9478b8b992 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 14:30:36 -0500 Subject: [PATCH 056/110] Cleanup events --- solgen/src/challenge/ChallengeManager.sol | 3 +-- solgen/src/challenge/IChallengeManager.sol | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 460e45a14a..706830bf59 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -272,12 +272,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { revert(NO_TURN); } } - event ChallengeTerminated(uint64 indexed challengeIndex); function clearChallenge(uint64 challengeIndex) external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); delete challenges[challengeIndex]; - emit ChallengeTerminated(challengeIndex); + emit ChallengedEnded(challengeIndex, ChallengeWinner.NONE, ChallengeTerminationType.CLEARED); } function currentResponder(uint64 challengeIndex) public view returns (address) { diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index 50aca04315..ad3b933d1c 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -30,7 +30,7 @@ interface IChallengeManager { enum ChallengeTerminationType { TIMEOUT, - CHALLENGER_TIMED_OUT + CLEARED } struct Challenge { From 2eeac8f83b1046af08eb343fadcb22e9184e7d24 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 14:35:55 -0500 Subject: [PATCH 057/110] Make takeTurn safe for functions that terminate --- solgen/src/challenge/ChallengeManager.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 706830bf59..a3003c1b9f 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -53,14 +53,17 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { _; - if (challenge.turn == Turn.CHALLENGER) { + if (challenge.turn == Turn.NO_CHALLENGE) { + // Early return since challenge must have terminated + return; + } else if (challenge.turn == Turn.CHALLENGER) { challenge.challengerTimeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.ASSERTER; } else if (challenge.turn == Turn.ASSERTER) { challenge.asserterTimeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.CHALLENGER; } else { - revert(NO_TURN); + revert("invalid turn state"); } challenge.lastMoveTimestamp = block.timestamp; } From ae3e7f08c60891d7772afad28324800641331ff5 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 14:48:17 -0500 Subject: [PATCH 058/110] Fix indentation --- solgen/src/challenge/ChallengeLib.sol | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 04b7eea777..d5be06e1d6 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -18,21 +18,18 @@ library ChallengeLib { pure returns (bytes32) { - ValueStack memory values; - { - // Start the value stack with the function call ABI for the entrypoint - Value[] memory startingValues = new Value[](3); - startingValues[0] = ValueLib.newRefNull(); - startingValues[1] = ValueLib.newI32(0); - startingValues[2] = ValueLib.newI32(0); - ValueArray memory valuesArray = ValueArray({ + // Start the value stack with the function call ABI for the entrypoint + Value[] memory startingValues = new Value[](3); + startingValues[0] = ValueLib.newRefNull(); + startingValues[1] = ValueLib.newI32(0); + startingValues[2] = ValueLib.newI32(0); + ValueArray memory valuesArray = ValueArray({ inner: startingValues - }); - values = ValueStack({ + }); + ValueStack memory values = ValueStack({ proved: valuesArray, remainingHash: 0 - }); - } + }); ValueStack memory internalStack; PcStack memory blocks; StackFrameWindow memory frameStack; From b3d559be17a75cde12e567a10e1444ee72f9cc10 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 15:26:33 -0500 Subject: [PATCH 059/110] Make participant struct --- solgen/src/challenge/ChallengeManager.sol | 38 ++++++++++++---------- solgen/src/challenge/IChallengeManager.sol | 15 ++++++--- solgen/src/mocks/ExecutionManager.sol | 12 ++++--- solgen/src/rollup/ValidatorUtils.sol | 14 +++----- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index a3003c1b9f..c1cabf016d 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -57,10 +57,10 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { // Early return since challenge must have terminated return; } else if (challenge.turn == Turn.CHALLENGER) { - challenge.challengerTimeLeft -= timeUsedSinceLastMove(challengeIndex); + challenge.challenger.timeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.ASSERTER; } else if (challenge.turn == Turn.ASSERTER) { - challenge.asserterTimeLeft -= timeUsedSinceLastMove(challengeIndex); + challenge.asserter.timeLeft -= timeUsedSinceLastMove(challengeIndex); challenge.turn = Turn.CHALLENGER; } else { revert("invalid turn state"); @@ -105,10 +105,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { // No need to set maxInboxMessages until execution challenge challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; challenge.startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; - challenge.asserter = asserter_; - challenge.challenger = challenger_; - challenge.asserterTimeLeft = asserterTimeLeft_; - challenge.challengerTimeLeft = challengerTimeLeft_; + challenge.asserter = Participant({ + addr: asserter_, + timeLeft: asserterTimeLeft_ + }); + challenge.challenger = Participant({ + addr: challenger_, + timeLeft: challengerTimeLeft_ + }); challenge.lastMoveTimestamp = block.timestamp; challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.BLOCK; @@ -279,15 +283,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function clearChallenge(uint64 challengeIndex) external override { require(msg.sender == address(resultReceiver), "NOT_RES_RECEIVER"); delete challenges[challengeIndex]; - emit ChallengedEnded(challengeIndex, ChallengeWinner.NONE, ChallengeTerminationType.CLEARED); + emit ChallengeEnded(challengeIndex, ChallengeTerminationType.CLEARED); } - function currentResponder(uint64 challengeIndex) public view returns (address) { + function currentResponder(uint64 challengeIndex) public view override returns (address) { Challenge storage challenge = challenges[challengeIndex]; if (challenge.turn == Turn.ASSERTER) { - return challenge.asserter; + return challenge.asserter.addr; } else if (challenge.turn == Turn.CHALLENGER) { - return challenge.challenger; + return challenge.challenger.addr; } else { revert(NO_TURN); } @@ -296,15 +300,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function currentResponderTimeLeft(uint64 challengeIndex) public override view returns (uint256) { Challenge storage challenge = challenges[challengeIndex]; if (challenge.turn == Turn.ASSERTER) { - return challenge.asserterTimeLeft; + return challenge.asserter.timeLeft; } else if (challenge.turn == Turn.CHALLENGER) { - return challenge.challengerTimeLeft; + return challenge.challenger.timeLeft; } else { revert(NO_TURN); } } - function isTimedOut(uint64 challengeIndex) public view returns (bool) { + function isTimedOut(uint64 challengeIndex) public view override returns (bool) { return timeUsedSinceLastMove(challengeIndex) > currentResponderTimeLeft(challengeIndex); } @@ -345,16 +349,16 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function _asserterWin(uint64 challengeIndex, ChallengeTerminationType reason) private { Challenge storage challenge = challenges[challengeIndex]; - resultReceiver.completeChallenge(challenge.asserter, challenge.challenger); + resultReceiver.completeChallenge(challenge.asserter.addr, challenge.challenger.addr); delete challenges[challengeIndex]; - emit ChallengedEnded(challengeIndex, ChallengeWinner.ASSERTER, reason); + emit ChallengeEnded(challengeIndex, reason); } function _challengerWin(uint64 challengeIndex, ChallengeTerminationType reason) private { Challenge storage challenge = challenges[challengeIndex]; - resultReceiver.completeChallenge(challenge.challenger, challenge.asserter); + resultReceiver.completeChallenge(challenge.challenger.addr, challenge.asserter.addr); delete challenges[challengeIndex]; - emit ChallengedEnded(challengeIndex, ChallengeWinner.CHALLENGER, reason); + emit ChallengeEnded(challengeIndex, reason); } function _currentWin(Challenge storage challenge) private { diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index ad3b933d1c..ca971f22cc 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -33,12 +33,15 @@ interface IChallengeManager { CLEARED } + struct Participant { + address addr; + uint256 timeLeft; + } + struct Challenge { - address asserter; - address challenger; + Participant asserter; + Participant challenger; - uint256 asserterTimeLeft; - uint256 challengerTimeLeft; uint256 lastMoveTimestamp; bytes32 wasmModuleRoot; @@ -64,7 +67,7 @@ interface IChallengeManager { event ExecutionChallengeBegun(uint64 indexed challengeIndex, uint256 blockSteps); event OneStepProofCompleted(uint64 indexed challengeIndex); - event ChallengedEnded(uint64 indexed challengeIndex, ChallengeWinner winner, ChallengeTerminationType kind); + event ChallengeEnded(uint64 indexed challengeIndex, ChallengeTerminationType kind); function initialize( IChallengeResultReceiver resultReceiver_, @@ -89,6 +92,8 @@ interface IChallengeManager { // function asserter() external view returns (address); // function challenger() external view returns (address); // function lastMoveTimestamp() external view returns (uint256); + function currentResponder(uint64 challengeIndex) external view returns (address); + function isTimedOut(uint64 challengeIndex) external view returns (bool); function currentResponderTimeLeft(uint64 challengeIndex_) external view returns (uint256); function clearChallenge(uint64 challengeIndex_) external; diff --git a/solgen/src/mocks/ExecutionManager.sol b/solgen/src/mocks/ExecutionManager.sol index a4d383a38f..23caa9bdc1 100644 --- a/solgen/src/mocks/ExecutionManager.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -25,10 +25,14 @@ contract SingleExecutionChallenge is ChallengeManager { segments[1] = startAndEndHashes[1]; bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); challenge.challengeStateHash = challengeStateHash; - challenge.asserter = asserter_; - challenge.challenger = challenger_; - challenge.asserterTimeLeft = asserterTimeLeft_; - challenge.challengerTimeLeft = challengerTimeLeft_; + challenge.asserter = Participant({ + addr: asserter_, + timeLeft: asserterTimeLeft_ + }); + challenge.challenger = Participant({ + addr: challenger_, + timeLeft: challengerTimeLeft_ + }); challenge.lastMoveTimestamp = block.timestamp; challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.EXECUTION; diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index 5aeaa77bd8..bf27f94bee 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -248,16 +248,10 @@ contract ValidatorUtils { for (uint256 i = 0; i < stakers.length; i++) { address staker = stakers[i]; uint64 challengeIndex = rollup.currentChallenge(staker); - if (challengeIndex != NO_CHAL_INDEX) { - IChallengeManager.Challenge memory challengeInfo = challengeManager.challengeInfo(challengeIndex); - uint256 timeSinceLastMove = block.timestamp - challengeInfo.lastMoveTimestamp; - if ( - timeSinceLastMove > challengeManager.currentResponderTimeLeft(challengeIndex) && - challengeInfo.asserter == staker - ) { - challenges[index] = challengeIndex; - index++; - } + if (challengeIndex != NO_CHAL_INDEX && + challengeManager.isTimedOut(challengeIndex) && + challengeManager.currentResponder(challengeIndex) == staker) { + challenges[index++] = challengeIndex; } } // Shrink array down to real size From 893297d429aa09dff5be9f33238357f80b052ca0 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 15:33:57 -0500 Subject: [PATCH 060/110] Switch to participant model in challenge --- solgen/src/challenge/ChallengeManager.sol | 57 +++++----------------- solgen/src/challenge/IChallengeManager.sol | 5 +- solgen/src/mocks/ExecutionManager.sol | 5 +- 3 files changed, 17 insertions(+), 50 deletions(-) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index c1cabf016d..e2466b6f4e 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -53,18 +53,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { _; - if (challenge.turn == Turn.NO_CHALLENGE) { + if (challenge.mode == ChallengeMode.NONE) { // Early return since challenge must have terminated return; - } else if (challenge.turn == Turn.CHALLENGER) { - challenge.challenger.timeLeft -= timeUsedSinceLastMove(challengeIndex); - challenge.turn = Turn.ASSERTER; - } else if (challenge.turn == Turn.ASSERTER) { - challenge.asserter.timeLeft -= timeUsedSinceLastMove(challengeIndex); - challenge.turn = Turn.CHALLENGER; - } else { - revert("invalid turn state"); } + + Participant memory current = challenge.current; + challenge.current = challenge.next; + challenge.next = current; + challenge.lastMoveTimestamp = block.timestamp; } @@ -105,16 +102,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { // No need to set maxInboxMessages until execution challenge challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; challenge.startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; - challenge.asserter = Participant({ + challenge.next = Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ }); - challenge.challenger = Participant({ + challenge.current = Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ }); challenge.lastMoveTimestamp = block.timestamp; - challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.BLOCK; emit InitiatedChallenge(challengeIndex); @@ -268,16 +264,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } function timeout(uint64 challengeIndex) external override { - Challenge storage challenge = challenges[challengeIndex]; require(isTimedOut(challengeIndex), "TIMEOUT_DEADLINE"); - - if (challenge.turn == Turn.ASSERTER) { - _challengerWin(challengeIndex, ChallengeTerminationType.TIMEOUT); - } else if (challenge.turn == Turn.CHALLENGER) { - _asserterWin(challengeIndex, ChallengeTerminationType.TIMEOUT); - } else { - revert(NO_TURN); - } + _nextWin(challengeIndex, ChallengeTerminationType.TIMEOUT); } function clearChallenge(uint64 challengeIndex) external override { @@ -288,24 +276,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function currentResponder(uint64 challengeIndex) public view override returns (address) { Challenge storage challenge = challenges[challengeIndex]; - if (challenge.turn == Turn.ASSERTER) { - return challenge.asserter.addr; - } else if (challenge.turn == Turn.CHALLENGER) { - return challenge.challenger.addr; - } else { - revert(NO_TURN); - } + return challenge.current.addr; } function currentResponderTimeLeft(uint64 challengeIndex) public override view returns (uint256) { Challenge storage challenge = challenges[challengeIndex]; - if (challenge.turn == Turn.ASSERTER) { - return challenge.asserter.timeLeft; - } else if (challenge.turn == Turn.CHALLENGER) { - return challenge.challenger.timeLeft; - } else { - revert(NO_TURN); - } + return challenge.current.timeLeft; } function isTimedOut(uint64 challengeIndex) public view override returns (bool) { @@ -347,16 +323,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ); } - function _asserterWin(uint64 challengeIndex, ChallengeTerminationType reason) private { - Challenge storage challenge = challenges[challengeIndex]; - resultReceiver.completeChallenge(challenge.asserter.addr, challenge.challenger.addr); - delete challenges[challengeIndex]; - emit ChallengeEnded(challengeIndex, reason); - } - - function _challengerWin(uint64 challengeIndex, ChallengeTerminationType reason) private { + function _nextWin(uint64 challengeIndex, ChallengeTerminationType reason) private { Challenge storage challenge = challenges[challengeIndex]; - resultReceiver.completeChallenge(challenge.challenger.addr, challenge.asserter.addr); + resultReceiver.completeChallenge(challenge.next.addr, challenge.current.addr); delete challenges[challengeIndex]; emit ChallengeEnded(challengeIndex, reason); } diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index ca971f22cc..d1f7eee33a 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -39,8 +39,8 @@ interface IChallengeManager { } struct Challenge { - Participant asserter; - Participant challenger; + Participant current; + Participant next; uint256 lastMoveTimestamp; @@ -50,7 +50,6 @@ interface IChallengeManager { bytes32 challengeStateHash; - Turn turn; ChallengeMode mode; } diff --git a/solgen/src/mocks/ExecutionManager.sol b/solgen/src/mocks/ExecutionManager.sol index 23caa9bdc1..359907511b 100644 --- a/solgen/src/mocks/ExecutionManager.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -25,16 +25,15 @@ contract SingleExecutionChallenge is ChallengeManager { segments[1] = startAndEndHashes[1]; bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); challenge.challengeStateHash = challengeStateHash; - challenge.asserter = Participant({ + challenge.next = Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ }); - challenge.challenger = Participant({ + challenge.current = Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ }); challenge.lastMoveTimestamp = block.timestamp; - challenge.turn = Turn.CHALLENGER; challenge.mode = ChallengeMode.EXECUTION; emit Bisected( From 1b619bbce88cddb3bb6cbcd498123b8cee74633a Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 15:54:31 -0500 Subject: [PATCH 061/110] Move functionality into ChallengeLib --- solgen/src/challenge/ChallengeLib.sol | 35 +++++++++++++++++ solgen/src/challenge/ChallengeManager.sol | 45 ++++++++++------------ solgen/src/challenge/IChallengeManager.sol | 31 ++------------- solgen/src/mocks/ExecutionManager.sol | 8 ++-- 4 files changed, 63 insertions(+), 56 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index d5be06e1d6..4e0b2419d3 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -2,10 +2,37 @@ pragma solidity ^0.8.0; import "../state/Machine.sol"; +import "../state/GlobalState.sol"; library ChallengeLib { using MachineLib for Machine; + enum ChallengeMode { + NONE, + BLOCK, + EXECUTION + } + + struct Participant { + address addr; + uint256 timeLeft; + } + + struct Challenge { + Participant current; + Participant next; + + uint256 lastMoveTimestamp; + + bytes32 wasmModuleRoot; + uint256 maxInboxMessages; + GlobalState[2] startAndEndGlobalStates; + + bytes32 challengeStateHash; + + ChallengeMode mode; + } + struct SegmentSelection { uint256 oldSegmentsStart; uint256 oldSegmentsLength; @@ -13,6 +40,14 @@ library ChallengeLib { uint256 challengePosition; } + function timeUsedSinceLastMove(Challenge storage challenge) internal view returns (uint256) { + return block.timestamp - challenge.lastMoveTimestamp; + } + + function isTimedOut(Challenge storage challenge) internal view returns (bool) { + return timeUsedSinceLastMove(challenge) > challenge.current.timeLeft; + } + function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) internal pure diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index e2466b6f4e..ac8829c2a7 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -13,12 +13,13 @@ import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; contract ChallengeManager is DelegateCallAware, IChallengeManager { using GlobalStateLib for GlobalState; using MachineLib for Machine; + using ChallengeLib for ChallengeLib.Challenge; string constant NO_TURN = "NO_TURN"; uint256 constant MAX_CHALLENGE_DEGREE = 40; uint64 public totalChallengesCreated; - mapping (uint256 => Challenge) public challenges; + mapping (uint256 => ChallengeLib.Challenge) public challenges; IChallengeResultReceiver public resultReceiver; @@ -26,12 +27,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { IBridge public delayedBridge; IOneStepProofEntry public osp; - function challengeInfo(uint64 challengeIndex) external view override returns (Challenge memory) { + function challengeInfo(uint64 challengeIndex) external view override returns (ChallengeLib.Challenge memory) { return challenges[challengeIndex]; } modifier takeTurn(uint64 challengeIndex, ChallengeLib.SegmentSelection calldata selection) { - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; require(msg.sender == currentResponder(challengeIndex), "CHAL_SENDER"); require(!isTimedOut(challengeIndex), "CHAL_DEADLINE"); @@ -53,12 +54,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { _; - if (challenge.mode == ChallengeMode.NONE) { + if (challenge.mode == ChallengeLib.ChallengeMode.NONE) { // Early return since challenge must have terminated return; } - Participant memory current = challenge.current; + ChallengeLib.Participant memory current = challenge.current; challenge.current = challenge.next; challenge.next = current; @@ -97,21 +98,21 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint64 challengeIndex = ++totalChallengesCreated; // The following is an assertion since it should never be possible, but it's an important invariant assert(challengeIndex != NO_CHAL_INDEX); - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; // No need to set maxInboxMessages until execution challenge challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; challenge.startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; - challenge.next = Participant({ + challenge.next = ChallengeLib.Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ }); - challenge.current = Participant({ + challenge.current = ChallengeLib.Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ }); challenge.lastMoveTimestamp = block.timestamp; - challenge.mode = ChallengeMode.BLOCK; + challenge.mode = ChallengeLib.ChallengeMode.BLOCK; emit InitiatedChallenge(challengeIndex); completeBisection( @@ -124,13 +125,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } function getStartGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { - Challenge storage challenge = challenges[challengeIndex]; - return challenge.startAndEndGlobalStates[0]; + return challenges[challengeIndex].startAndEndGlobalStates[0]; } function getEndGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { - Challenge storage challenge = challenges[challengeIndex]; - return challenge.startAndEndGlobalStates[1]; + return challenges[challengeIndex].startAndEndGlobalStates[1]; } /** @@ -187,7 +186,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ) ); - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); @@ -223,7 +222,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } challenge.maxInboxMessages = challenge.maxInboxMessages; - challenge.mode = ChallengeMode.EXECUTION; + challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; completeBisection( challengeIndex, @@ -240,7 +239,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ChallengeLib.SegmentSelection calldata selection, bytes calldata proof ) external takeTurn(challengeIndex, selection) { - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); @@ -275,21 +274,19 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } function currentResponder(uint64 challengeIndex) public view override returns (address) { - Challenge storage challenge = challenges[challengeIndex]; - return challenge.current.addr; + return challenges[challengeIndex].current.addr; } function currentResponderTimeLeft(uint64 challengeIndex) public override view returns (uint256) { - Challenge storage challenge = challenges[challengeIndex]; - return challenge.current.timeLeft; + return challenges[challengeIndex].current.timeLeft; } function isTimedOut(uint64 challengeIndex) public view override returns (bool) { - return timeUsedSinceLastMove(challengeIndex) > currentResponderTimeLeft(challengeIndex); + return challenges[challengeIndex].isTimedOut(); } function timeUsedSinceLastMove(uint64 challengeIndex) private view returns (uint256) { - return block.timestamp - challenges[challengeIndex].lastMoveTimestamp; + return challenges[challengeIndex].timeUsedSinceLastMove(); } function requireValidBisection( @@ -324,13 +321,13 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { } function _nextWin(uint64 challengeIndex, ChallengeTerminationType reason) private { - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; resultReceiver.completeChallenge(challenge.next.addr, challenge.current.addr); delete challenges[challengeIndex]; emit ChallengeEnded(challengeIndex, reason); } - function _currentWin(Challenge storage challenge) private { + function _currentWin(ChallengeLib.Challenge storage challenge) private { // As a safety measure, challenges can only be resolved by timeouts during mainnet beta. // As state is 0, no move is possible. The other party will lose via timeout challenge.challengeStateHash = bytes32(0); diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index d1f7eee33a..b167ec7a5b 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -1,7 +1,6 @@ //SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import "../state/GlobalState.sol"; import "../state/Machine.sol"; import "../bridge/IBridge.sol"; import "../bridge/ISequencerInbox.sol"; @@ -9,6 +8,8 @@ import "../osp/IOneStepProofEntry.sol"; import "./IChallengeResultReceiver.sol"; +import "./ChallengeLib.sol"; + interface IChallengeManager { enum Turn { NO_CHALLENGE, @@ -16,12 +17,6 @@ interface IChallengeManager { CHALLENGER } - enum ChallengeMode { - NONE, - BLOCK, - EXECUTION - } - enum ChallengeWinner { NONE, ASSERTER, @@ -33,26 +28,6 @@ interface IChallengeManager { CLEARED } - struct Participant { - address addr; - uint256 timeLeft; - } - - struct Challenge { - Participant current; - Participant next; - - uint256 lastMoveTimestamp; - - bytes32 wasmModuleRoot; - uint256 maxInboxMessages; - GlobalState[2] startAndEndGlobalStates; - - bytes32 challengeStateHash; - - ChallengeMode mode; - } - event InitiatedChallenge(uint64 indexed challengeIndex); event Bisected( @@ -86,7 +61,7 @@ interface IChallengeManager { uint256 challengerTimeLeft_ ) external returns (uint64); - function challengeInfo(uint64 challengeIndex_) external view returns (Challenge memory); + function challengeInfo(uint64 challengeIndex_) external view returns (ChallengeLib.Challenge memory); // function asserter() external view returns (address); // function challenger() external view returns (address); diff --git a/solgen/src/mocks/ExecutionManager.sol b/solgen/src/mocks/ExecutionManager.sol index 359907511b..9ea4165616 100644 --- a/solgen/src/mocks/ExecutionManager.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -18,23 +18,23 @@ contract SingleExecutionChallenge is ChallengeManager { osp = osp_; resultReceiver = resultReceiver_; uint64 challengeIndex = ++totalChallengesCreated; - Challenge storage challenge = challenges[challengeIndex]; + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.maxInboxMessages = maxInboxMessagesRead_; bytes32[] memory segments = new bytes32[](2); segments[0] = startAndEndHashes[0]; segments[1] = startAndEndHashes[1]; bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); challenge.challengeStateHash = challengeStateHash; - challenge.next = Participant({ + challenge.next = ChallengeLib.Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ }); - challenge.current = Participant({ + challenge.current = ChallengeLib.Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ }); challenge.lastMoveTimestamp = block.timestamp; - challenge.mode = ChallengeMode.EXECUTION; + challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; emit Bisected( challengeIndex, From e4c972bc8ea4b2ba36beddab1afd1cc54f05c089 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 16:33:10 -0500 Subject: [PATCH 062/110] Remove start global state --- solgen/src/challenge/ChallengeLib.sol | 2 +- solgen/src/challenge/ChallengeManager.sol | 22 ++++++--------- solgen/src/challenge/IChallengeManager.sol | 2 +- validator/block_challenge_backend.go | 14 ++++----- validator/challenge_manager.go | 33 +++++++++++++++++++--- 5 files changed, 45 insertions(+), 28 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 4e0b2419d3..a37f6fcd26 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -26,7 +26,7 @@ library ChallengeLib { bytes32 wasmModuleRoot; uint256 maxInboxMessages; - GlobalState[2] startAndEndGlobalStates; + GlobalState endGlobalState; bytes32 challengeStateHash; diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index ac8829c2a7..9e453aaf29 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -101,8 +101,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; // No need to set maxInboxMessages until execution challenge - challenge.startAndEndGlobalStates[0] = startAndEndGlobalStates_[0]; - challenge.startAndEndGlobalStates[1] = startAndEndGlobalStates_[1]; + challenge.endGlobalState = startAndEndGlobalStates_[1]; challenge.next = ChallengeLib.Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ @@ -114,7 +113,10 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { challenge.lastMoveTimestamp = block.timestamp; challenge.mode = ChallengeLib.ChallengeMode.BLOCK; - emit InitiatedChallenge(challengeIndex); + emit InitiatedChallenge( + challengeIndex, + startAndEndGlobalStates_[0] + ); completeBisection( challengeIndex, 0, @@ -124,14 +126,6 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { return challengeIndex; } - function getStartGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { - return challenges[challengeIndex].startAndEndGlobalStates[0]; - } - - function getEndGlobalState(uint64 challengeIndex) external view returns (GlobalState memory) { - return challenges[challengeIndex].startAndEndGlobalStates[1]; - } - /** * @notice Initiate the next round in the bisection by objecting to execution correctness with a bisection * of an execution segment with the same length but a different endpoint. This is either the initial move @@ -216,12 +210,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[1] ); - uint256 maxInboxMessagesRead = challenge.startAndEndGlobalStates[1].getInboxPosition(); - if (machineStatuses[1] == MachineStatus.ERRORED || challenge.startAndEndGlobalStates[1].getPositionInMessage() > 0) { + uint256 maxInboxMessagesRead = challenge.endGlobalState.getInboxPosition(); + if (machineStatuses[1] == MachineStatus.ERRORED || challenge.endGlobalState.getPositionInMessage() > 0) { maxInboxMessagesRead++; } - challenge.maxInboxMessages = challenge.maxInboxMessages; + challenge.maxInboxMessages = maxInboxMessagesRead; challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; completeBisection( diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index b167ec7a5b..54ae1d62ed 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -28,7 +28,7 @@ interface IChallengeManager { CLEARED } - event InitiatedChallenge(uint64 indexed challengeIndex); + event InitiatedChallenge(uint64 indexed challengeIndex, GlobalState startState); event Bisected( uint64 indexed challengeIndex, diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 3e2a10cc53..fd05f1a087 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -82,6 +82,7 @@ var _ ChallengeBackend = (*BlockChallengeBackend)(nil) func NewBlockChallengeBackend( ctx context.Context, challengeIndex uint64, + solStartGs challengegen.GlobalState, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, @@ -91,13 +92,14 @@ func NewBlockChallengeBackend( callOpts := &bind.CallOpts{Context: ctx} challengeCon, err := challengegen.NewChallengeManager(challengeAddr, client) if err != nil { - return nil, err + return nil, errors.WithStack(err) } - solStartGs, err := challengeCon.GetStartGlobalState(callOpts, challengeIndex) + info, err := challengeCon.ChallengeInfo(callOpts, challengeIndex) if err != nil { - return nil, err + return nil, errors.WithStack(err) } + startGs := GoGlobalStateFromSolidity(solStartGs) startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNumber) if startGs.BlockHash != (common.Hash{}) { @@ -121,11 +123,7 @@ func NewBlockChallengeBackend( return nil, fmt.Errorf("start block %v and start message count %v don't correspond", startBlockNum, startMsgCount) } - solEndGs, err := challengeCon.GetEndGlobalState(callOpts, challengeIndex) - if err != nil { - return nil, err - } - endGs := GoGlobalStateFromSolidity(solEndGs) + endGs := GoGlobalStateFromSolidity(info.EndGlobalState) var endMsgCount arbutil.MessageIndex if endGs.Batch > 0 { endMsgCount, err = inboxTracker.GetBatchMessageCount(endGs.Batch - 1) diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index dea8ae533c..01122130d3 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -85,10 +85,30 @@ func NewChallengeManager( targetNumMachines int, confirmationBlocks int64, ) (*ChallengeManager, error) { - challengeCoreCon, err := challengegen.NewChallengeManager(challengeManagerAddr, l1client) + con, err := challengegen.NewChallengeManager(challengeManagerAddr, l1client) + if err != nil { + return nil, err + } + + logs, err := l1client.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: new(big.Int).SetUint64(startL1Block), + Addresses: []common.Address{challengeManagerAddr}, + Topics: [][]common.Hash{{challengeBisectedID}, {uint64ToIndex(challengeIndex)}}, + }) if err != nil { return nil, err } + if len(logs) == 0 { + return nil, errors.New("didn't find InitiatedChallenge event") + } + // Multiple logs are in theory fine, as they should all reveal the same preimage. + // We'll use the most recent log to be safe. + evmLog := logs[len(logs)-1] + parsedLog, err := con.ParseInitiatedChallenge(evmLog) + if err != nil { + return nil, err + } + genesisBlockNum, err := txStreamer.GetGenesisBlockNumber() if err != nil { return nil, err @@ -96,6 +116,7 @@ func NewChallengeManager( backend, err := NewBlockChallengeBackend( ctx, challengeIndex, + parsedLog.StartState, l2blockChain, inboxTracker, l1client, @@ -106,7 +127,7 @@ func NewChallengeManager( return nil, err } return &ChallengeManager{ - con: challengeCoreCon, + con: con, challengeManagerAddr: challengeManagerAddr, challengeIndex: challengeIndex, client: l1client, @@ -186,12 +207,16 @@ func (m *ChallengeManager) ChallengeIndex() uint64 { return m.challengeIndex } -func (m *ChallengeManager) challengeFilterIndex() common.Hash { +func uint64ToIndex(val uint64) common.Hash { var challengeIndex common.Hash - binary.BigEndian.PutUint64(challengeIndex[(32-8):], m.challengeIndex) + binary.BigEndian.PutUint64(challengeIndex[(32-8):], val) return challengeIndex } +func (m *ChallengeManager) challengeFilterIndex() common.Hash { + return uint64ToIndex(m.challengeIndex) +} + // Given the challenge's state hash, resolve the full challenge state via the Bisected event. func (m *ChallengeManager) resolveStateHash(ctx context.Context, stateHash common.Hash) (ChallengeState, error) { logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ From 2e770eeaa4150b7418e378b6b65377fb61e08b81 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 16:43:02 -0500 Subject: [PATCH 063/110] Store less of global state --- solgen/src/challenge/ChallengeLib.sol | 5 +++-- solgen/src/challenge/ChallengeManager.sol | 10 ++++++---- solgen/src/challenge/IChallengeManager.sol | 2 +- validator/block_challenge_backend.go | 13 +++---------- validator/challenge_manager.go | 3 +-- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index a37f6fcd26..26e6ff5797 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -26,10 +26,11 @@ library ChallengeLib { bytes32 wasmModuleRoot; uint256 maxInboxMessages; - GlobalState endGlobalState; - bytes32 challengeStateHash; + uint64 globalEndInboxPosition; + uint64 globalEndMessagePosition; + ChallengeMode mode; } diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 9e453aaf29..e3bf643730 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -101,7 +101,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; // No need to set maxInboxMessages until execution challenge - challenge.endGlobalState = startAndEndGlobalStates_[1]; + challenge.globalEndInboxPosition = startAndEndGlobalStates_[1].getInboxPosition(); + challenge.globalEndMessagePosition = startAndEndGlobalStates_[1].getPositionInMessage(); challenge.next = ChallengeLib.Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ @@ -115,7 +116,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { emit InitiatedChallenge( challengeIndex, - startAndEndGlobalStates_[0] + startAndEndGlobalStates_[0], + startAndEndGlobalStates_[1] ); completeBisection( challengeIndex, @@ -210,8 +212,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[1] ); - uint256 maxInboxMessagesRead = challenge.endGlobalState.getInboxPosition(); - if (machineStatuses[1] == MachineStatus.ERRORED || challenge.endGlobalState.getPositionInMessage() > 0) { + uint256 maxInboxMessagesRead = challenge.globalEndInboxPosition; + if (machineStatuses[1] == MachineStatus.ERRORED || challenge.globalEndMessagePosition > 0) { maxInboxMessagesRead++; } diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index 54ae1d62ed..b729aedffa 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -28,7 +28,7 @@ interface IChallengeManager { CLEARED } - event InitiatedChallenge(uint64 indexed challengeIndex, GlobalState startState); + event InitiatedChallenge(uint64 indexed challengeIndex, GlobalState startState, GlobalState endState); event Bisected( uint64 indexed challengeIndex, diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index fd05f1a087..590abc2096 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -80,27 +80,20 @@ type BlockChallengeBackend struct { var _ ChallengeBackend = (*BlockChallengeBackend)(nil) func NewBlockChallengeBackend( - ctx context.Context, challengeIndex uint64, - solStartGs challengegen.GlobalState, + initialState *challengegen.ChallengeManagerInitiatedChallenge, bc *core.BlockChain, inboxTracker InboxTrackerInterface, client bind.ContractBackend, challengeAddr common.Address, genesisBlockNumber uint64, ) (*BlockChallengeBackend, error) { - callOpts := &bind.CallOpts{Context: ctx} challengeCon, err := challengegen.NewChallengeManager(challengeAddr, client) if err != nil { return nil, errors.WithStack(err) } - info, err := challengeCon.ChallengeInfo(callOpts, challengeIndex) - if err != nil { - return nil, errors.WithStack(err) - } - - startGs := GoGlobalStateFromSolidity(solStartGs) + startGs := GoGlobalStateFromSolidity(initialState.StartState) startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNumber) if startGs.BlockHash != (common.Hash{}) { startBlock := bc.GetBlockByHash(startGs.BlockHash) @@ -123,7 +116,7 @@ func NewBlockChallengeBackend( return nil, fmt.Errorf("start block %v and start message count %v don't correspond", startBlockNum, startMsgCount) } - endGs := GoGlobalStateFromSolidity(info.EndGlobalState) + endGs := GoGlobalStateFromSolidity(initialState.EndState) var endMsgCount arbutil.MessageIndex if endGs.Batch > 0 { endMsgCount, err = inboxTracker.GetBatchMessageCount(endGs.Batch - 1) diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 01122130d3..6ab75b18ad 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -114,9 +114,8 @@ func NewChallengeManager( return nil, err } backend, err := NewBlockChallengeBackend( - ctx, challengeIndex, - parsedLog.StartState, + parsedLog, l2blockChain, inboxTracker, l1client, From 608d398e90852f8d08b4adba2aea39c8a91e84b3 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 16:45:59 -0500 Subject: [PATCH 064/110] Rearrange struct to shrink size --- solgen/src/challenge/ChallengeLib.sol | 2 +- solgen/src/challenge/ChallengeManager.sol | 2 +- solgen/src/mocks/ExecutionManager.sol | 2 +- validator/challenge_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 26e6ff5797..e4a6d805af 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -25,11 +25,11 @@ library ChallengeLib { uint256 lastMoveTimestamp; bytes32 wasmModuleRoot; - uint256 maxInboxMessages; bytes32 challengeStateHash; uint64 globalEndInboxPosition; uint64 globalEndMessagePosition; + uint64 maxInboxMessages; ChallengeMode mode; } diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index e3bf643730..15249e6f03 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -212,7 +212,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[1] ); - uint256 maxInboxMessagesRead = challenge.globalEndInboxPosition; + uint64 maxInboxMessagesRead = challenge.globalEndInboxPosition; if (machineStatuses[1] == MachineStatus.ERRORED || challenge.globalEndMessagePosition > 0) { maxInboxMessagesRead++; } diff --git a/solgen/src/mocks/ExecutionManager.sol b/solgen/src/mocks/ExecutionManager.sol index 9ea4165616..947cd0216e 100644 --- a/solgen/src/mocks/ExecutionManager.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -7,7 +7,7 @@ contract SingleExecutionChallenge is ChallengeManager { constructor( IOneStepProofEntry osp_, IChallengeResultReceiver resultReceiver_, - uint256 maxInboxMessagesRead_, + uint64 maxInboxMessagesRead_, bytes32[2] memory startAndEndHashes, uint256 numSteps_, address asserter_, diff --git a/validator/challenge_test.go b/validator/challenge_test.go index a05eb56f33..63525f52a5 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -64,7 +64,7 @@ func CreateChallenge( client, ospEntry, resultReceiverAddr, - new(big.Int).SetUint64(^uint64(0)), + ^uint64(0), [2][32]byte{startHashBytes, endHashBytes}, big.NewInt(int64(endMachineSteps)), asserter, From 84472bfad6c1db4b2a3e5966960067b9b239616b Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 17:05:50 -0500 Subject: [PATCH 065/110] Fix full challenge test --- system_tests/full_challenge_test.go | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index fc0021b342..52e319a9ca 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -81,7 +81,7 @@ func CreateChallenge( t.Fatal(err) } - _, tx, challengeFactory, err := challengegen.DeployChallengeFactory(auth, client, ospEntry) + challengeManagerAddr, tx, challengeManager, err := challengegen.DeployChallengeManager(auth, client) if err != nil { t.Fatal(err) } @@ -89,14 +89,14 @@ func CreateChallenge( if err != nil { t.Fatal(err) } + tx, err = challengeManager.Initialize(auth, resultReceiverAddr, sequencerInbox, delayedBridge, ospEntry) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) + if err != nil { + t.Fatal(err) + } - tx, err = challengeFactory.CreateChallenge( + tx, err = challengeManager.CreateChallenge( auth, - challengegen.IChallengeFactoryChallengeContracts{ - ResultReceiver: resultReceiverAddr, - SequencerInbox: sequencerInbox, - DelayedBridge: delayedBridge, - }, wasmModuleRoot, [2]uint8{ validator.StatusFinished, @@ -112,17 +112,12 @@ func CreateChallenge( big.NewInt(100000), big.NewInt(100000), ) - receipt, err := arbutil.EnsureTxSucceeded(context.Background(), client, tx) - if err != nil { - t.Fatal(err) - } - - challengeCreatedEvent, err := challengeFactory.ParseChallengeCreated(*receipt.Logs[len(receipt.Logs)-1]) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) } - return resultReceiver, challengeCreatedEvent.Challenge + return resultReceiver, challengeManagerAddr } func writeTxToBatch(writer io.Writer, tx *types.Transaction) error { @@ -299,7 +294,7 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { } numBlocks := asserterLatestBlock.NumberU64() - asserterGenesis.NumberU64() - resultReceiver, challenge := CreateChallenge( + resultReceiver, challengeManagerAddr := CreateChallenge( t, &deployerTxOpts, l1Backend, @@ -315,12 +310,12 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { ) confirmLatestBlock(ctx, t, l1Info, l1Backend) - asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challenge, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) + asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challengeManagerAddr, 0, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } - challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challenge, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) + challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challengeManagerAddr, 0, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } From 718f8bbb17ee7865d34bab05bb2732cd4331aaeb Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 17:20:05 -0500 Subject: [PATCH 066/110] Cleanup backends --- validator/block_challenge_backend.go | 25 +++------- validator/challenge_manager.go | 58 +++++++++++++----------- validator/challenge_test.go | 2 + validator/execution_challenge_backend.go | 16 ++----- 4 files changed, 44 insertions(+), 57 deletions(-) diff --git a/validator/block_challenge_backend.go b/validator/block_challenge_backend.go index 590abc2096..f4b12055a1 100644 --- a/validator/block_challenge_backend.go +++ b/validator/block_challenge_backend.go @@ -10,7 +10,6 @@ import ( "fmt" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core" @@ -62,9 +61,6 @@ func (s GoGlobalState) AsSolidityStruct() challengegen.GlobalState { } type BlockChallengeBackend struct { - challengeCon *challengegen.ChallengeManager - client bind.ContractBackend - challengeIndex uint64 bc *core.BlockChain startBlock int64 startPosition uint64 @@ -80,19 +76,11 @@ type BlockChallengeBackend struct { var _ ChallengeBackend = (*BlockChallengeBackend)(nil) func NewBlockChallengeBackend( - challengeIndex uint64, initialState *challengegen.ChallengeManagerInitiatedChallenge, bc *core.BlockChain, inboxTracker InboxTrackerInterface, - client bind.ContractBackend, - challengeAddr common.Address, genesisBlockNumber uint64, ) (*BlockChallengeBackend, error) { - challengeCon, err := challengegen.NewChallengeManager(challengeAddr, client) - if err != nil { - return nil, errors.WithStack(err) - } - startGs := GoGlobalStateFromSolidity(initialState.StartState) startBlockNum := arbutil.MessageCountToBlockNumber(0, genesisBlockNumber) if startGs.BlockHash != (common.Hash{}) { @@ -105,6 +93,7 @@ func NewBlockChallengeBackend( var startMsgCount arbutil.MessageIndex if startGs.Batch > 0 { + var err error startMsgCount, err = inboxTracker.GetBatchMessageCount(startGs.Batch - 1) if err != nil { return nil, errors.Wrap(err, "failed to get challenge start batch metadata") @@ -119,6 +108,7 @@ func NewBlockChallengeBackend( endGs := GoGlobalStateFromSolidity(initialState.EndState) var endMsgCount arbutil.MessageIndex if endGs.Batch > 0 { + var err error endMsgCount, err = inboxTracker.GetBatchMessageCount(endGs.Batch - 1) if err != nil { return nil, errors.Wrap(err, "failed to get challenge end batch metadata") @@ -127,9 +117,6 @@ func NewBlockChallengeBackend( endMsgCount += arbutil.MessageIndex(endGs.PosInBatch) return &BlockChallengeBackend{ - client: client, - challengeIndex: challengeIndex, - challengeCon: challengeCon, bc: bc, startBlock: startBlockNum, startGs: startGs, @@ -263,7 +250,7 @@ func (b *BlockChallengeBackend) GetHashAtStep(_ context.Context, position uint64 } func (b *BlockChallengeBackend) IssueExecChallenge( - auth *bind.TransactOpts, + core *challengeCore, oldState *ChallengeState, startSegment int, numsteps uint64, @@ -284,9 +271,9 @@ func (b *BlockChallengeBackend) IssueExecChallenge( globalStates[0].Hash(), globalStates[1].Hash(), } - return b.challengeCon.ChallengeExecution( - auth, - b.challengeIndex, + return core.con.ChallengeExecution( + core.auth, + core.challengeIndex, challengegen.ChallengeLibSegmentSelection{ OldSegmentsStart: oldState.Start, OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 6ab75b18ad..830d6a9c16 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -25,6 +25,7 @@ const maxBisectionDegree uint64 = 40 const challengeModeExecution = 2 +var initiatedChallengeID common.Hash var challengeBisectedID common.Hash var executionChallengeBegunID common.Hash @@ -33,6 +34,7 @@ func init() { if err != nil { panic(err) } + initiatedChallengeID = parsedChallengeManagerABI.Events["ChallengeInitiated"].ID challengeBisectedID = parsedChallengeManagerABI.Events["Bisected"].ID executionChallengeBegunID = parsedChallengeManagerABI.Events["ExecutionChallengeBegun"].ID } @@ -42,8 +44,7 @@ type ChallengeBackend interface { GetHashAtStep(ctx context.Context, position uint64) (common.Hash, error) } -type ChallengeManager struct { - // fields used in both block and execution challenge +type challengeCore struct { con *challengegen.ChallengeManager challengeManagerAddr common.Address challengeIndex uint64 @@ -52,6 +53,11 @@ type ChallengeManager struct { actingAs common.Address startL1Block *big.Int confirmationBlocks int64 +} + +type ChallengeManager struct { + // fields used in both block and execution challenge + *challengeCore // fields below are used while working on block challenge blockChallengeBackend *BlockChallengeBackend @@ -93,7 +99,7 @@ func NewChallengeManager( logs, err := l1client.FilterLogs(ctx, ethereum.FilterQuery{ FromBlock: new(big.Int).SetUint64(startL1Block), Addresses: []common.Address{challengeManagerAddr}, - Topics: [][]common.Hash{{challengeBisectedID}, {uint64ToIndex(challengeIndex)}}, + Topics: [][]common.Hash{{initiatedChallengeID}, {uint64ToIndex(challengeIndex)}}, }) if err != nil { return nil, err @@ -114,32 +120,31 @@ func NewChallengeManager( return nil, err } backend, err := NewBlockChallengeBackend( - challengeIndex, parsedLog, l2blockChain, inboxTracker, - l1client, - challengeManagerAddr, genesisBlockNum, ) if err != nil { return nil, err } return &ChallengeManager{ - con: con, - challengeManagerAddr: challengeManagerAddr, - challengeIndex: challengeIndex, - client: l1client, - auth: auth, - actingAs: fromAddr, - startL1Block: new(big.Int).SetUint64(startL1Block), + challengeCore: &challengeCore{ + con: con, + challengeManagerAddr: challengeManagerAddr, + challengeIndex: challengeIndex, + client: l1client, + auth: auth, + actingAs: fromAddr, + startL1Block: new(big.Int).SetUint64(startL1Block), + confirmationBlocks: confirmationBlocks, + }, blockChallengeBackend: backend, inboxReader: inboxReader, inboxTracker: inboxTracker, txStreamer: txStreamer, blockchain: l2blockChain, targetNumMachines: targetNumMachines, - confirmationBlocks: confirmationBlocks, }, nil } @@ -152,6 +157,7 @@ func NewExecutionChallengeManager( initialMachine MachineInterface, startL1Block uint64, targetNumMachines int, + confirmationBlocks int64, ) (*ChallengeManager, error) { con, err := challengegen.NewChallengeManager(challengeManagerAddr, l1client) if err != nil { @@ -162,13 +168,16 @@ func NewExecutionChallengeManager( return nil, err } return &ChallengeManager{ - con: con, - challengeManagerAddr: challengeManagerAddr, - challengeIndex: challengeIndex, - client: l1client, - auth: auth, - actingAs: auth.From, - startL1Block: new(big.Int).SetUint64(startL1Block), + challengeCore: &challengeCore{ + con: con, + challengeManagerAddr: challengeManagerAddr, + challengeIndex: challengeIndex, + client: l1client, + auth: auth, + actingAs: auth.From, + startL1Block: new(big.Int).SetUint64(startL1Block), + confirmationBlocks: confirmationBlocks, + }, executionChallengeBackend: backend, }, nil } @@ -527,10 +536,7 @@ func (m *ChallengeManager) Act(ctx context.Context) (*types.Transaction, error) log.Info("sending onestepproof", "challenge", m.challengeIndex, "startPosition", startPosition, "endPosition", endPosition) return m.executionChallengeBackend.IssueOneStepProof( ctx, - m.client, - m.auth, - m.challengeIndex, - m.challengeManagerAddr, + m.challengeCore, state, nextMovePos, ) @@ -557,7 +563,7 @@ func (m *ChallengeManager) Act(ctx context.Context) (*types.Transaction, error) stepCount = stepCountMachine.GetStepCount() log.Info("issuing one step proof", "challenge", m.challengeIndex, "stepCount", stepCount, "blockNum", blockNum) return m.blockChallengeBackend.IssueExecChallenge( - m.auth, + m.challengeCore, state, nextMovePos, stepCount, diff --git a/validator/challenge_test.go b/validator/challenge_test.go index 63525f52a5..59b1a91d3f 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -157,6 +157,7 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step asserterMachine, 0, 4, + 12, ) Require(t, err) @@ -168,6 +169,7 @@ func runChallengeTest(t *testing.T, wasmPath string, wasmLibPaths []string, step challengerMachine, 0, 4, + 12, ) Require(t, err) diff --git a/validator/execution_challenge_backend.go b/validator/execution_challenge_backend.go index b1051089dc..83d46b66ad 100644 --- a/validator/execution_challenge_backend.go +++ b/validator/execution_challenge_backend.go @@ -8,7 +8,6 @@ import ( "context" "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/offchainlabs/arbstate/solgen/go/challengegen" @@ -89,25 +88,18 @@ func (b *ExecutionChallengeBackend) GetHashAtStep(ctx context.Context, position func (b *ExecutionChallengeBackend) IssueOneStepProof( ctx context.Context, - client bind.ContractBackend, - auth *bind.TransactOpts, - challengeIndex uint64, - challengeManager common.Address, + core *challengeCore, oldState *ChallengeState, startSegment int, ) (*types.Transaction, error) { - con, err := challengegen.NewChallengeManager(challengeManager, client) - if err != nil { - return nil, err - } mach, err := b.getMachineAt(ctx, oldState.Segments[startSegment].Position) if err != nil { return nil, err } proof := mach.ProveNextStep() - return con.OneStepProveExecution( - auth, - challengeIndex, + return core.con.OneStepProveExecution( + core.auth, + core.challengeIndex, challengegen.ChallengeLibSegmentSelection{ OldSegmentsStart: oldState.Start, OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start), From 879f4b3c66744f37b57b8011cf4644e92c401a01 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 17:29:23 -0500 Subject: [PATCH 067/110] Remove unneeded method --- validator/challenge_manager.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 830d6a9c16..2be3fc4df1 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -221,16 +221,12 @@ func uint64ToIndex(val uint64) common.Hash { return challengeIndex } -func (m *ChallengeManager) challengeFilterIndex() common.Hash { - return uint64ToIndex(m.challengeIndex) -} - // Given the challenge's state hash, resolve the full challenge state via the Bisected event. func (m *ChallengeManager) resolveStateHash(ctx context.Context, stateHash common.Hash) (ChallengeState, error) { logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ FromBlock: m.startL1Block, Addresses: []common.Address{m.challengeManagerAddr}, - Topics: [][]common.Hash{{challengeBisectedID}, {m.challengeFilterIndex()}, {stateHash}}, + Topics: [][]common.Hash{{challengeBisectedID}, {uint64ToIndex(m.challengeIndex)}, {stateHash}}, }) if err != nil { return ChallengeState{}, err @@ -468,7 +464,7 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { logs, err := m.client.FilterLogs(ctx, ethereum.FilterQuery{ FromBlock: m.startL1Block, Addresses: []common.Address{m.challengeManagerAddr}, - Topics: [][]common.Hash{{executionChallengeBegunID}, {m.challengeFilterIndex()}}, + Topics: [][]common.Hash{{executionChallengeBegunID}, {uint64ToIndex(m.challengeIndex)}}, }) if err != nil { return errors.WithStack(err) From 7612a867026748b7c89ff91272c6ecb8f3ca0515 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 02:26:36 -0500 Subject: [PATCH 068/110] Add rollup methods for atomic staking --- solgen/src/rollup/RollupUserLogic.sol | 45 ++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 476f7a1457..8389c4160d 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -146,7 +146,7 @@ abstract contract AbsRollupUserLogic is * @param nodeHash Node hash of nodeNum (protects against reorgs) */ function stakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) - external + public onlyValidator whenNotPaused { @@ -174,7 +174,7 @@ abstract contract AbsRollupUserLogic is RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, uint256 prevNodeInboxMaxCount - ) external onlyValidator whenNotPaused { + ) public onlyValidator whenNotPaused { require(isStaked(msg.sender), "NOT_STAKED"); // Ensure staker is staked on the previous node uint64 prevNode = latestStakedNode(msg.sender); @@ -642,13 +642,27 @@ contract RollupUserLogic is AbsRollupUserLogic { /** * @notice Create a new stake - * @dev It is recomended to call stakeOnExistingNode after creating a new stake + * @dev It is recommended to call stakeOnExistingNode after creating a new stake * so that a griefer doesn't remove your stake by immediately calling returnOldDeposit */ - function newStake() external payable onlyValidator whenNotPaused { + function newStake() public payable onlyValidator whenNotPaused { _newStake(msg.value); } + function newstakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable { + newStake(); + stakeOnExistingNode(nodeNum, nodeHash); + } + + function newstakeOnNewNode( + RollupLib.Assertion calldata assertion, + bytes32 expectedNodeHash, + uint256 prevNodeInboxMaxCount + ) external payable { + newStake(); + stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); + } + /** * @notice Increase the amount staked eth for the given staker * @param stakerAddress Address of the staker whose stake is increased @@ -663,7 +677,7 @@ contract RollupUserLogic is AbsRollupUserLogic { } /** - * @notice Withdraw uncomitted funds owned by sender from the rollup chain + * @notice Withdraw uncommitted funds owned by sender from the rollup chain * @param destination Address to transfer the withdrawn funds to */ function withdrawStakerFunds(address payable destination) @@ -690,12 +704,12 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic { /** * @notice Create a new stake - * @dev It is recomended to call stakeOnExistingNode after creating a new stake + * @dev It is recommended to call stakeOnExistingNode after creating a new stake * so that a griefer doesn't remove your stake by immediately calling returnOldDeposit * @param tokenAmount the amount of tokens staked */ function newStake(uint256 tokenAmount) - external + public onlyValidator whenNotPaused { @@ -710,6 +724,21 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic { ); } + function newstakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external payable { + newStake(tokenAmount); + stakeOnExistingNode(nodeNum, nodeHash); + } + + function newstakeOnNewNode( + uint256 tokenAmount, + RollupLib.Assertion calldata assertion, + bytes32 expectedNodeHash, + uint256 prevNodeInboxMaxCount + ) external payable { + newStake(tokenAmount); + stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); + } + /** * @notice Increase the amount staked tokens for the given staker * @param stakerAddress Address of the staker whose stake is increased @@ -732,7 +761,7 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic { } /** - * @notice Withdraw uncomitted funds owned by sender from the rollup chain + * @notice Withdraw uncommitted funds owned by sender from the rollup chain * @param destination Address to transfer the withdrawn funds to */ function withdrawStakerFunds(address payable destination) From 46bedf214742f852cceb60e6a4fb8d0d884483b1 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 11:39:45 -0500 Subject: [PATCH 069/110] Respond to PR review comments --- solgen/src/challenge/ChallengeLib.sol | 4 ++- solgen/src/challenge/ChallengeManager.sol | 27 ++++++++++--------- solgen/src/challenge/IChallengeManager.sol | 17 ++---------- .../challenge/IChallengeResultReceiver.sol | 2 +- solgen/src/libraries/Constants.sol | 2 +- solgen/src/mocks/MockResultReceiver.sol | 8 +++--- solgen/src/rollup/RollupCore.sol | 4 +-- solgen/src/rollup/RollupUserLogic.sol | 6 +++-- 8 files changed, 32 insertions(+), 38 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index e4a6d805af..8f86aa3a2d 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -6,7 +6,9 @@ import "../state/GlobalState.sol"; library ChallengeLib { using MachineLib for Machine; + using ChallengeLib for Challenge; + /// @dev It's assumed that that uninitialzed challenges have mode NONE enum ChallengeMode { NONE, BLOCK, @@ -46,7 +48,7 @@ library ChallengeLib { } function isTimedOut(Challenge storage challenge) internal view returns (bool) { - return timeUsedSinceLastMove(challenge) > challenge.current.timeLeft; + return challenge.timeUsedSinceLastMove() > challenge.current.timeLeft; } function getStartMachineHash(bytes32 globalStateHash, bytes32 wasmModuleRoot) diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 15249e6f03..10e330a7e4 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -193,7 +193,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[0] == globalStateHashes[1], "HALTED_CHANGE" ); - _currentWin(challenge); + _currentWin(challengeIndex, ChallengeTerminationType.BLOCK_PROOF); return; } @@ -255,7 +255,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ); emit OneStepProofCompleted(challengeIndex); - _currentWin(challenge); + _currentWin(challengeIndex, ChallengeTerminationType.EXECUTION_PROOF); } function timeout(uint64 challengeIndex) external override { @@ -316,24 +316,25 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ); } + /// @dev This function causes the mode of the challenge to be set to NONE by deleting the challenge function _nextWin(uint64 challengeIndex, ChallengeTerminationType reason) private { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; - resultReceiver.completeChallenge(challenge.next.addr, challenge.current.addr); delete challenges[challengeIndex]; + resultReceiver.completeChallenge(challengeIndex, challenge.next.addr, challenge.current.addr); emit ChallengeEnded(challengeIndex, reason); } - function _currentWin(ChallengeLib.Challenge storage challenge) private { - // As a safety measure, challenges can only be resolved by timeouts during mainnet beta. - // As state is 0, no move is possible. The other party will lose via timeout + /** + * @dev this currently sets a challenge hash of 0 - no move is possible for the next participant to progress the + * state. It is assumed that wherever this function is consumed, the turn is then adjusted for the opposite party + * to timeout. This is done as a safety measure so challenges can only be resolved by timeouts during mainnet beta. + */ + function _currentWin(uint64 challengeIndex, ChallengeTerminationType /* reason */) private { + ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.challengeStateHash = bytes32(0); - // if (turn == Turn.ASSERTER) { - // _asserterWin(); - // } else if (turn == Turn.CHALLENGER) { - // _challengerWin(); - // } else { - // revert(NO_TURN); - // } +// delete challenges[challengeIndex]; +// resultReceiver.completeChallenge(challengeIndex, challenge.current.addr, challenge.next.addr); +// emit ChallengeEnded(challengeIndex, reason); } } diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index b729aedffa..b2e2876d07 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -11,20 +11,10 @@ import "./IChallengeResultReceiver.sol"; import "./ChallengeLib.sol"; interface IChallengeManager { - enum Turn { - NO_CHALLENGE, - ASSERTER, - CHALLENGER - } - - enum ChallengeWinner { - NONE, - ASSERTER, - CHALLENGER - } - enum ChallengeTerminationType { TIMEOUT, + BLOCK_PROOF, + EXECUTION_PROOF, CLEARED } @@ -63,9 +53,6 @@ interface IChallengeManager { function challengeInfo(uint64 challengeIndex_) external view returns (ChallengeLib.Challenge memory); -// function asserter() external view returns (address); -// function challenger() external view returns (address); -// function lastMoveTimestamp() external view returns (uint256); function currentResponder(uint64 challengeIndex) external view returns (address); function isTimedOut(uint64 challengeIndex) external view returns (bool); function currentResponderTimeLeft(uint64 challengeIndex_) external view returns (uint256); diff --git a/solgen/src/challenge/IChallengeResultReceiver.sol b/solgen/src/challenge/IChallengeResultReceiver.sol index b78014244c..07f386fd73 100644 --- a/solgen/src/challenge/IChallengeResultReceiver.sol +++ b/solgen/src/challenge/IChallengeResultReceiver.sol @@ -2,5 +2,5 @@ pragma solidity ^0.8.0; interface IChallengeResultReceiver { - function completeChallenge(address winner, address loser) external; + function completeChallenge(uint256 challengeIndex, address winner, address loser) external; } diff --git a/solgen/src/libraries/Constants.sol b/solgen/src/libraries/Constants.sol index 200fc85068..c7eafdf45b 100644 --- a/solgen/src/libraries/Constants.sol +++ b/solgen/src/libraries/Constants.sol @@ -21,4 +21,4 @@ pragma solidity ^0.8.4; // 90% of Geth's 128KB tx size limit, leaving ~13KB for proving uint256 constant MAX_DATA_SIZE = 117964; -uint256 constant NO_CHAL_INDEX = 0; \ No newline at end of file +uint64 constant NO_CHAL_INDEX = 0; \ No newline at end of file diff --git a/solgen/src/mocks/MockResultReceiver.sol b/solgen/src/mocks/MockResultReceiver.sol index 3dd7d7d6f4..92ca92948c 100644 --- a/solgen/src/mocks/MockResultReceiver.sol +++ b/solgen/src/mocks/MockResultReceiver.sol @@ -6,12 +6,14 @@ import "../challenge/IChallengeResultReceiver.sol"; contract MockResultReceiver is IChallengeResultReceiver { address public winner; address public loser; + uint256 public challengeIndex; - event ChallengeCompleted(address indexed challenge, address indexed winner, address indexed loser); +event ChallengeCompleted(uint256 indexed challengeIndex, address indexed winner, address indexed loser); - function completeChallenge(address winner_, address loser_) external override { + function completeChallenge(uint256 challengeIndex_, address winner_, address loser_) external override { winner = winner_; loser = loser_; - emit ChallengeCompleted(msg.sender, winner_, loser_); + challengeIndex = challengeIndex_; + emit ChallengeCompleted(challengeIndex, winner_, loser_); } } diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 3db2871bec..88a5d5b1c0 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -326,7 +326,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { depositAmount, stakerIndex, _latestConfirmed, - 0, // new staker is not in challenge + NO_CHAL_INDEX, // new staker is not in challenge true ); _lastStakeBlock = uint64(block.number); @@ -358,7 +358,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { */ function clearChallenge(address stakerAddress) internal { Staker storage staker = _stakerMap[stakerAddress]; - staker.currentChallenge = 0; + staker.currentChallenge = NO_CHAL_INDEX; } /** diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 4a15d951ca..d7492f4fb6 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -382,13 +382,15 @@ abstract contract AbsRollupUserLogic is * @param winningStaker Address of the winning staker * @param losingStaker Address of the losing staker */ - function completeChallenge(address winningStaker, address losingStaker) + function completeChallenge(uint256 challengeIndex, address winningStaker, address losingStaker) external override whenNotPaused { // Only the challenge manager contract can call this to declare the winner and loser require(msg.sender == address(challengeManager), "WRONG_SENDER"); + require(currentChallenge(winningStaker) == challengeIndex, "WIN_WRONG_CHAL"); + require(currentChallenge(losingStaker) == challengeIndex, "LOSE_WRONG_CHAL"); completeChallengeImpl(winningStaker, losingStaker); } @@ -612,7 +614,7 @@ abstract contract AbsRollupUserLogic is function requireUnchallengedStaker(address stakerAddress) private view { require(isStaked(stakerAddress), "NOT_STAKED"); require( - currentChallenge(stakerAddress) == 0, + currentChallenge(stakerAddress) == NO_CHAL_INDEX, "IN_CHAL" ); } From 1e0d731d3fb2f93c1db54747702fd988f6d145ab Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 12:12:16 -0500 Subject: [PATCH 070/110] Add newStakeOnExistingNode and newStakeOnNewNode to interface --- solgen/src/libraries/ArbitrumProxy.sol | 2 +- solgen/src/rollup/IRollupLogic.sol | 22 +++++++++++++++++++++- solgen/src/rollup/RollupUserLogic.sol | 18 +++++++++--------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/solgen/src/libraries/ArbitrumProxy.sol b/solgen/src/libraries/ArbitrumProxy.sol index 27dd2c5fd5..ef457fdc6a 100644 --- a/solgen/src/libraries/ArbitrumProxy.sol +++ b/solgen/src/libraries/ArbitrumProxy.sol @@ -29,7 +29,7 @@ contract ArbitrumProxy is AdminFallbackProxy { address(connectedContracts.rollupAdminLogic), abi.encodeWithSelector(IRollupAdmin.initialize.selector, config, connectedContracts), address(connectedContracts.rollupUserLogic), - abi.encodeWithSelector(IRollupUser.initialize.selector, config.stakeToken), + abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken), config.owner ) {} } diff --git a/solgen/src/rollup/IRollupLogic.sol b/solgen/src/rollup/IRollupLogic.sol index 13a6acb2eb..816c6f08a2 100644 --- a/solgen/src/rollup/IRollupLogic.sol +++ b/solgen/src/rollup/IRollupLogic.sol @@ -22,7 +22,7 @@ import "./RollupLib.sol"; import "../bridge/ISequencerInbox.sol"; import "../bridge/IOutbox.sol"; -interface IRollupUser { +interface IRollupUserAbs { /// @dev the user logic just validated configuration and shouldn't write to state during init /// this allows the admin logic to ensure consistency on parameters. function initialize(address stakeToken) external view; @@ -38,6 +38,26 @@ interface IRollupUser { function countStakedZombies(uint64 nodeNum) external view returns (uint256); } +interface IRollupUser is IRollupUserAbs { + function newStakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable; + function newStakeOnNewNode( + RollupLib.Assertion calldata assertion, + bytes32 expectedNodeHash, + uint256 prevNodeInboxMaxCount + ) external payable; +} + +interface IRollupUserERC20 is IRollupUserAbs { + function newStakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external; + + function newStakeOnNewNode( + uint256 tokenAmount, + RollupLib.Assertion calldata assertion, + bytes32 expectedNodeHash, + uint256 prevNodeInboxMaxCount + ) external; +} + interface IRollupAdmin { event OwnerFunctionCalled(uint256 indexed id); diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 8389c4160d..bbc0503c31 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -11,7 +11,7 @@ import "./RollupCore.sol"; abstract contract AbsRollupUserLogic is RollupCore, UUPSNotUpgradeable, - IRollupUser, + IRollupUserAbs, IChallengeResultReceiver { using NodeLib for Node; @@ -632,7 +632,7 @@ abstract contract AbsRollupUserLogic is returns (uint256); } -contract RollupUserLogic is AbsRollupUserLogic { +contract RollupUserLogic is AbsRollupUserLogic, IRollupUser { /// @dev the user logic just validated configuration and shouldn't write to state during init /// this allows the admin logic to ensure consistency on parameters. function initialize(address _stakeToken) external view override onlyProxy { @@ -649,16 +649,16 @@ contract RollupUserLogic is AbsRollupUserLogic { _newStake(msg.value); } - function newstakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable { + function newStakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable override { newStake(); stakeOnExistingNode(nodeNum, nodeHash); } - function newstakeOnNewNode( + function newStakeOnNewNode( RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, uint256 prevNodeInboxMaxCount - ) external payable { + ) external payable override { newStake(); stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); } @@ -694,7 +694,7 @@ contract RollupUserLogic is AbsRollupUserLogic { } } -contract ERC20RollupUserLogic is AbsRollupUserLogic { +contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { /// @dev the user logic just validated configuration and shouldn't write to state during init /// this allows the admin logic to ensure consistency on parameters. function initialize(address _stakeToken) external view override onlyProxy { @@ -724,17 +724,17 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic { ); } - function newstakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external payable { + function newStakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external override { newStake(tokenAmount); stakeOnExistingNode(nodeNum, nodeHash); } - function newstakeOnNewNode( + function newStakeOnNewNode( uint256 tokenAmount, RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, uint256 prevNodeInboxMaxCount - ) external payable { + ) external override { newStake(tokenAmount); stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); } From e041a636cfb1d37a1a398cedf04d5656298c4b3f Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 12:51:49 -0500 Subject: [PATCH 071/110] Remove unsafe newStake method --- solgen/src/rollup/RollupUserLogic.sol | 80 +++++++++++----------- validator/l1_validator.go | 1 + validator/staker.go | 98 ++++++++++++++------------- 3 files changed, 94 insertions(+), 85 deletions(-) diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index bbc0503c31..92c13413b1 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -641,25 +641,27 @@ contract RollupUserLogic is AbsRollupUserLogic, IRollupUser { } /** - * @notice Create a new stake - * @dev It is recommended to call stakeOnExistingNode after creating a new stake - * so that a griefer doesn't remove your stake by immediately calling returnOldDeposit + * @notice Create a new stake on an existing node + * @param nodeNum Number of the node your stake will be place one + * @param nodeHash Node hash of the node with the given nodeNum */ - function newStake() public payable onlyValidator whenNotPaused { - _newStake(msg.value); - } - function newStakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable override { - newStake(); + _newStake(msg.value); stakeOnExistingNode(nodeNum, nodeHash); } + /** + * @notice Create a new stake on a new node + * @param assertion Assertion describing the state change between the old node and the new one + * @param expectedNodeHash Node hash of the node that will be created + * @param prevNodeInboxMaxCount Total of messages in the inbox as of the previous node + */ function newStakeOnNewNode( RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, uint256 prevNodeInboxMaxCount ) external payable override { - newStake(); + _newStake(msg.value); stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); } @@ -703,40 +705,35 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { } /** - * @notice Create a new stake - * @dev It is recommended to call stakeOnExistingNode after creating a new stake - * so that a griefer doesn't remove your stake by immediately calling returnOldDeposit - * @param tokenAmount the amount of tokens staked + * @notice Create a new stake on an existing node + * @param tokenAmount Amount of the rollups staking token to stake + * @param nodeNum Number of the node your stake will be place one + * @param nodeHash Node hash of the node with the given nodeNum */ - function newStake(uint256 tokenAmount) - public - onlyValidator - whenNotPaused - { - _newStake(tokenAmount); - require( - IERC20Upgradeable(stakeToken).transferFrom( - msg.sender, - address(this), - tokenAmount - ), - "TRANSFER_FAIL" - ); - } - function newStakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external override { - newStake(tokenAmount); + _newStake(tokenAmount); stakeOnExistingNode(nodeNum, nodeHash); + /// @dev This is an external call, safe because it's at the end of the function + receiveTokens(tokenAmount); } + /** + * @notice Create a new stake on a new node + * @param tokenAmount Amount of the rollups staking token to stake + * @param assertion Assertion describing the state change between the old node and the new one + * @param expectedNodeHash Node hash of the node that will be created + * @param prevNodeInboxMaxCount Total of messages in the inbox as of the previous node + */ function newStakeOnNewNode( uint256 tokenAmount, RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, uint256 prevNodeInboxMaxCount ) external override { - newStake(tokenAmount); + _newStake(tokenAmount); stakeOnNewNode(assertion, expectedNodeHash, prevNodeInboxMaxCount); + /// @dev This is an external call, safe because it's at the end of the function + receiveTokens(tokenAmount); } /** @@ -750,14 +747,8 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { whenNotPaused { _addToDeposit(stakerAddress, tokenAmount); - require( - IERC20Upgradeable(stakeToken).transferFrom( - msg.sender, - address(this), - tokenAmount - ), - "TRANSFER_FAIL" - ); + /// @dev This is an external call, safe because it's at the end of the function + receiveTokens(tokenAmount); } /** @@ -779,4 +770,15 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { ); return amount; } + + function receiveTokens(uint256 tokenAmount) private { + require( + IERC20Upgradeable(stakeToken).transferFrom( + msg.sender, + address(this), + tokenAmount + ), + "TRANSFER_FAIL" + ); + } } diff --git a/validator/l1_validator.go b/validator/l1_validator.go index de2a0b7fa3..50f579ae34 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -211,6 +211,7 @@ type OurStakerInfo struct { LatestStakedNode uint64 LatestStakedNodeHash [32]byte CanProgress bool + StakeExists bool *StakerInfo } diff --git a/validator/staker.go b/validator/staker.go index ef221e06d0..f0d70059ac 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -247,6 +247,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { LatestStakedNode: latestStakedNode, LatestStakedNodeHash: latestStakedNodeHash, StakerInfo: rawInfo, + StakeExists: rawInfo != nil, } effectiveStrategy := s.strategy @@ -316,26 +317,16 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } } - // Don't attempt to create a new stake if we're resolving a node, - // as that might affect the current required stake. - creatingNewStake := rawInfo == nil && s.builder.BuilderTransactionCount() == 0 - if creatingNewStake { - if err := s.newStake(ctx); err != nil { - return nil, err - } - } - if rawInfo != nil { if err = s.handleConflict(ctx, rawInfo); err != nil { return nil, err } } - if rawInfo != nil || creatingNewStake { - // Advance stake up to 20 times in one transaction - for i := 0; info.CanProgress && i < 20; i++ { - if err := s.advanceStake(ctx, &info, effectiveStrategy); err != nil { - return nil, err - } + + // Advance stake up to 20 times in one transaction + for i := 0; info.CanProgress && i < 20; i++ { + if err := s.advanceStake(ctx, &info, effectiveStrategy); err != nil { + return nil, err } } if rawInfo != nil && s.builder.BuilderTransactionCount() == 0 { @@ -345,14 +336,11 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } txCount := s.builder.BuilderTransactionCount() - if creatingNewStake { - // Ignore our stake creation, as it's useless by itself - txCount-- - } if txCount == 0 { return nil, nil } - if creatingNewStake { + + if info.StakerInfo == nil && info.StakeExists { log.Info("staking to execute transactions") } return s.wallet.ExecuteTransactions(ctx, s.builder) @@ -384,28 +372,6 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { return err } -func (s *Staker) newStake(ctx context.Context) error { - var addr = s.wallet.Address() - if addr != nil { - info, err := s.rollup.StakerInfo(ctx, *addr) - if err != nil { - return err - } - if info != nil { - return nil - } - } - stakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx)) - if err != nil { - return err - } - _, err = s.rollup.NewStake(s.builder.AuthWithAmount(ctx, stakeAmount)) - if err != nil { - return err - } - return nil -} - func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { active := effectiveStrategy >= StakeLatestStrategy action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy) @@ -438,8 +404,29 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv info.CanProgress = false info.LatestStakedNode = 0 info.LatestStakedNodeHash = action.hash - _, err = s.rollup.StakeOnNewNode(s.builder.Auth(ctx), action.assertion.AsSolidityStruct(), action.hash, action.prevInboxMaxCount) - return err + + // We'll return early if we already havea stake + if info.StakeExists { + _, err = s.rollup.StakeOnNewNode(s.builder.Auth(ctx), action.assertion.AsSolidityStruct(), action.hash, action.prevInboxMaxCount) + return err + } + + // If we have no stake yet, we'll put one down + stakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx)) + if err != nil { + return err + } + _, err = s.rollup.NewStakeOnNewNode( + s.builder.AuthWithAmount(ctx, stakeAmount), + action.assertion.AsSolidityStruct(), + action.hash, + action.prevInboxMaxCount, + ) + if err != nil { + return err + } + info.StakeExists = true + return nil case existingNodeAction: info.LatestStakedNode = action.number info.LatestStakedNodeHash = action.hash @@ -457,8 +444,27 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv return nil } log.Info("staking on existing node", "node", action.number) - _, err = s.rollup.StakeOnExistingNode(s.builder.Auth(ctx), action.number, action.hash) - return err + // We'll return early if we already havea stake + if info.StakeExists { + _, err = s.rollup.StakeOnExistingNode(s.builder.Auth(ctx), action.number, action.hash) + return err + } + + // If we have no stake yet, we'll put one down + stakeAmount, err := s.rollup.CurrentRequiredStake(s.getCallOpts(ctx)) + if err != nil { + return err + } + _, err = s.rollup.NewStakeOnExistingNode( + s.builder.AuthWithAmount(ctx, stakeAmount), + action.number, + action.hash, + ) + if err != nil { + return err + } + info.StakeExists = true + return nil default: panic("invalid action type") } From 5c49b8c7444247a9c44478035f4d861d6bd2bced Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 13:02:13 -0500 Subject: [PATCH 072/110] Use inChallenge function in completeChallenge --- solgen/src/rollup/RollupUserLogic.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index d7492f4fb6..3c8c75fe4d 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -389,8 +389,7 @@ abstract contract AbsRollupUserLogic is { // Only the challenge manager contract can call this to declare the winner and loser require(msg.sender == address(challengeManager), "WRONG_SENDER"); - require(currentChallenge(winningStaker) == challengeIndex, "WIN_WRONG_CHAL"); - require(currentChallenge(losingStaker) == challengeIndex, "LOSE_WRONG_CHAL"); + require (challengeIndex == inChallenge(winningStaker, losingStaker)); completeChallengeImpl(winningStaker, losingStaker); } From a7e1985cddc92b86d8f4e770eee1aca4d069a3f7 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 13:13:41 -0500 Subject: [PATCH 073/110] Move setting maxInboxMessagesRead into challenge creation --- solgen/src/challenge/ChallengeLib.sol | 6 ------ solgen/src/challenge/ChallengeManager.sol | 15 ++++++--------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index 8f86aa3a2d..dbb2255da4 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -23,16 +23,10 @@ library ChallengeLib { struct Challenge { Participant current; Participant next; - uint256 lastMoveTimestamp; - bytes32 wasmModuleRoot; bytes32 challengeStateHash; - - uint64 globalEndInboxPosition; - uint64 globalEndMessagePosition; uint64 maxInboxMessages; - ChallengeMode mode; } diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 10e330a7e4..1eb75b85da 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -100,9 +100,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { assert(challengeIndex != NO_CHAL_INDEX); ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; - // No need to set maxInboxMessages until execution challenge - challenge.globalEndInboxPosition = startAndEndGlobalStates_[1].getInboxPosition(); - challenge.globalEndMessagePosition = startAndEndGlobalStates_[1].getPositionInMessage(); + + uint64 maxInboxMessagesRead = startAndEndGlobalStates_[1].getInboxPosition(); + if (startAndEndMachineStatuses_[1] == MachineStatus.ERRORED || startAndEndGlobalStates_[1].getPositionInMessage() > 0) { + maxInboxMessagesRead++; + } + challenge.maxInboxMessages = maxInboxMessagesRead; challenge.next = ChallengeLib.Participant({ addr: asserter_, timeLeft: asserterTimeLeft_ @@ -212,12 +215,6 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[1] ); - uint64 maxInboxMessagesRead = challenge.globalEndInboxPosition; - if (machineStatuses[1] == MachineStatus.ERRORED || challenge.globalEndMessagePosition > 0) { - maxInboxMessagesRead++; - } - - challenge.maxInboxMessages = maxInboxMessagesRead; challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; completeBisection( From 39e0c8ba53d309bf5d729ebf33d916219d68f5c7 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 15:04:48 -0500 Subject: [PATCH 074/110] Don't create stake if we're resolving a node --- validator/l1_validator.go | 16 ++++++++-------- validator/staker.go | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 50f579ae34..391b41a2df 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -148,36 +148,36 @@ func (v *Validator) resolveTimedOutChallenges(ctx context.Context) (*types.Trans return v.wallet.TimeoutChallenges(ctx, challengesToEliminate) } -func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo) error { +func (v *Validator) resolveNextNode(ctx context.Context, info *StakerInfo) (bool, error) { callOpts := v.getCallOpts(ctx) confirmType, err := v.validatorUtils.CheckDecidableNextNode(callOpts, v.rollupAddress) if err != nil { - return err + return false, err } unresolvedNodeIndex, err := v.rollup.FirstUnresolvedNode(callOpts) if err != nil { - return err + return false, err } switch ConfirmType(confirmType) { case CONFIRM_TYPE_INVALID: addr := v.wallet.Address() if info == nil || addr == nil || info.LatestStakedNode <= unresolvedNodeIndex { // We aren't an example of someone staked on a competitor - return nil + return false, nil } log.Info("rejecing node", "node", unresolvedNodeIndex) _, err = v.rollup.RejectNextNode(v.builder.Auth(ctx), *addr) - return err + return true, err case CONFIRM_TYPE_VALID: nodeInfo, err := v.rollup.LookupNode(ctx, unresolvedNodeIndex) if err != nil { - return err + return false, err } afterGs := nodeInfo.AfterState().GlobalState _, err = v.rollup.ConfirmNextNode(v.builder.Auth(ctx), afterGs.BlockHash, afterGs.SendRoot) - return err + return true, err default: - return nil + return false, nil } } diff --git a/validator/staker.go b/validator/staker.go index f0d70059ac..10e5c65d30 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -288,6 +288,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { return nil, err } } + resolvingNode := false if shouldResolveNodes { // Keep the stake of this validator placed if we plan on staking further arbTx, err := s.removeOldStakers(ctx, effectiveStrategy >= StakeLatestStrategy) @@ -298,7 +299,8 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { if err != nil || arbTx != nil { return arbTx, err } - if err := s.resolveNextNode(ctx, rawInfo); err != nil { + resolvingNode, err = s.resolveNextNode(ctx, rawInfo) + if err != nil { return nil, err } } @@ -323,12 +325,17 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } } - // Advance stake up to 20 times in one transaction - for i := 0; info.CanProgress && i < 20; i++ { - if err := s.advanceStake(ctx, &info, effectiveStrategy); err != nil { - return nil, err + // Don't attempt to create a new stake if we're resolving a node, + // as that might affect the current required stake. + if rawInfo != nil || !resolvingNode { + // Advance stake up to 20 times in one transaction + for i := 0; info.CanProgress && i < 20; i++ { + if err := s.advanceStake(ctx, &info, effectiveStrategy); err != nil { + return nil, err + } } } + if rawInfo != nil && s.builder.BuilderTransactionCount() == 0 { if err := s.createConflict(ctx, rawInfo); err != nil { return nil, err From ff3efcf115cb0f493a5b145b6cb4c33ea9fe1126 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 15:43:34 -0500 Subject: [PATCH 075/110] Fix sequencer inbox stub --- solgen/src/mocks/SequencerInboxStub.sol | 161 +++--------------------- system_tests/full_challenge_test.go | 22 +++- 2 files changed, 34 insertions(+), 149 deletions(-) diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index c478fdbd08..9f70d84415 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -5,165 +5,32 @@ pragma solidity ^0.8.0; -import "../bridge/IBridge.sol"; -import "../bridge/Messages.sol"; -import "../bridge/ISequencerInbox.sol"; -import "../libraries/IGasRefunder.sol"; +import "../bridge/SequencerInbox.sol"; -contract SequencerInboxStub is ISequencerInbox { - bytes32[] public override inboxAccs; - uint256 public totalDelayedMessagesRead; +contract SequencerInboxStub is SequencerInbox { + constructor(IBridge delayedBridge_, address sequencer_, ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) { + delayedBridge = delayedBridge_; + rollup = msg.sender; + maxTimeVariation = maxTimeVariation_; + isBatchPoster[sequencer_] = true; - IBridge public delayedBridge; - - mapping(address => bool) public isBatchPoster; - - constructor(IBridge _delayedBridge, address _sequencer) { - delayedBridge = _delayedBridge; - isBatchPoster[_sequencer] = true; - - bytes memory header = abi.encodePacked( - uint64(0), - uint64(0), - uint64(0), - uint64(0), - uint64(0) - ); - bytes32 headerHash = keccak256(header); - bytes32 acc = keccak256(abi.encodePacked(bytes32(0), headerHash, bytes32(0))); - inboxAccs.push(acc); - bytes memory data; - TimeBounds memory timeBounds; - emit SequencerBatchDelivered(0, bytes32(0), acc, bytes32(0), 0, timeBounds, data); - } - - function addSequencerL2BatchFromOrigin( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder - ) external { - // solhint-disable-next-line avoid-tx-origin - require(msg.sender == tx.origin, "ORIGIN_ONLY"); - require(isBatchPoster[msg.sender], "NOT_BATCH_POSTER"); - - uint256 calldataSize; - assembly { - calldataSize := calldatasize() - } - - require(inboxAccs.length == sequenceNumber, "BAD_SEQ_NUM"); ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 afterAcc - ) = addSequencerL2BatchImpl(data, afterDelayedMessagesRead); - - TimeBounds memory emptyTimeBounds; - emptyTimeBounds.maxTimestamp = ~uint64(0); - emptyTimeBounds.maxBlockNumber = ~uint64(0); - emit SequencerBatchDeliveredFromOrigin( - inboxAccs.length - 1, - beforeAcc, - afterAcc, - delayedAcc, - totalDelayedMessagesRead, - emptyTimeBounds - ); - } - - function addSequencerL2Batch( - uint256 sequenceNumber, - bytes calldata data, - uint256 afterDelayedMessagesRead, - IGasRefunder - ) external override { - require(isBatchPoster[msg.sender], "NOT_BATCH_POSTER"); - - require(inboxAccs.length == sequenceNumber, "BAD_SEQ_NUM"); + bytes32 dataHash, + TimeBounds memory timeBounds + ) = formEmptyDataHash(0); ( bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc - ) = addSequencerL2BatchImpl(data, afterDelayedMessagesRead); - TimeBounds memory emptyTimeBounds; - emptyTimeBounds.maxTimestamp = ~uint64(0); - emptyTimeBounds.maxBlockNumber = ~uint64(0); + ) = addSequencerL2BatchImpl(dataHash, 0); emit SequencerBatchDelivered( inboxAccs.length - 1, beforeAcc, afterAcc, delayedAcc, - afterDelayedMessagesRead, - emptyTimeBounds, - data - ); - } - - function addSequencerL2BatchImpl( - bytes calldata data, - uint256 afterDelayedMessagesRead - ) - internal - returns ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 acc - ) - { - require( - afterDelayedMessagesRead >= totalDelayedMessagesRead, - "DELAYED_BACKWARDS" - ); - require( - delayedBridge.messageCount() >= afterDelayedMessagesRead, - "DELAYED_TOO_FAR" - ); - - uint256 fullDataLen = 40 + data.length; - require(fullDataLen >= 40, "DATA_LEN_OVERFLOW"); - bytes memory fullData = new bytes(fullDataLen); - - bytes memory header = abi.encodePacked( - uint64(0), - ~uint64(0), - uint64(0), - ~uint64(0), - uint64(afterDelayedMessagesRead) + totalDelayedMessagesRead, + timeBounds, + BatchDataLocation.NoData ); - require(header.length == 40, "BAD_HEADER_LEN"); - for (uint256 i = 0; i < 40; i++) { - fullData[i] = header[i]; - } - // copy data into fullData at offset 40 (the extra 32 offset is because solidity puts the array len first) - assembly { - calldatacopy(add(fullData, 72), data.offset, data.length) - } - - if (inboxAccs.length > 0) { - beforeAcc = inboxAccs[inboxAccs.length - 1]; - } - if (afterDelayedMessagesRead > 0) { - delayedAcc = delayedBridge.inboxAccs(afterDelayedMessagesRead - 1); - } - bytes32 fullDataHash = keccak256(fullData); - acc = keccak256(abi.encodePacked(beforeAcc, fullDataHash, delayedAcc)); - inboxAccs.push(acc); - totalDelayedMessagesRead = afterDelayedMessagesRead; - } - - function batchCount() external view override returns (uint256) { - return inboxAccs.length; - } - - function setMaxTimeVariation( - ISequencerInbox.MaxTimeVariation memory timeVariation - ) external override {} - - function setIsBatchPoster(address addr, bool isBatchPoster_) - external - override - { - isBatchPoster[addr] = isBatchPoster_; } } diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 52e319a9ca..c964e51ad0 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -222,11 +222,29 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { t.Fatal(err) } - asserterSeqInboxAddr, _, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer")) + timeBounds := mocksgen.ISequencerInboxMaxTimeVariation{ + DelayBlocks: big.NewInt(10000), + FutureBlocks: big.NewInt(10000), + DelaySeconds: big.NewInt(10000), + FutureSeconds: big.NewInt(10000), + } + asserterSeqInboxAddr, _, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub( + &deployerTxOpts, + l1Backend, + delayedBridge, + l1Info.GetAddress("sequencer"), + timeBounds, + ) if err != nil { t.Fatal(err) } - challengerSeqInboxAddr, _, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub(&deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer")) + challengerSeqInboxAddr, _, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub( + &deployerTxOpts, + l1Backend, + delayedBridge, + l1Info.GetAddress("sequencer"), + timeBounds, + ) if err != nil { t.Fatal(err) } From 492c235063ccf47b9dc2ea69ea9532169d6d0c33 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 15:45:02 -0500 Subject: [PATCH 076/110] Update go-ethereum dep --- arbnode/inbox_tracker.go | 4 ++-- go-ethereum | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index 4b018715d1..dc0b9e7af4 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -335,7 +335,7 @@ type multiplexerBackend struct { positionWithinMessage uint64 ctx context.Context - client L1Interface + client arbutil.L1Interface inbox *InboxTracker } @@ -375,7 +375,7 @@ func (b *multiplexerBackend) ReadDelayedInbox(seqNum uint64) ([]byte, error) { var delayedMessagesMismatch = errors.New("sequencer batch delayed messages missing or different") -func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client L1Interface, batches []*SequencerInboxBatch) error { +func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client arbutil.L1Interface, batches []*SequencerInboxBatch) error { if len(batches) == 0 { return nil } diff --git a/go-ethereum b/go-ethereum index bdf5436449..1e9c9b8613 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit bdf5436449917d6147f9531d3fbd4737ed7b52b1 +Subproject commit 1e9c9b86135dafebf7ab84641a5674e4249ee849 From 18a8ebb7c738875674b148833cedf0e6df4ab9d2 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 15:50:01 -0500 Subject: [PATCH 077/110] Add missing error check --- system_tests/full_challenge_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index c964e51ad0..254a543746 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -90,6 +90,9 @@ func CreateChallenge( t.Fatal(err) } tx, err = challengeManager.Initialize(auth, resultReceiverAddr, sequencerInbox, delayedBridge, ospEntry) + if err != nil { + t.Fatal(err) + } _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) if err != nil { t.Fatal(err) From 0f17ba97db57e78aab0c952f106a8170feb98d1b Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 15:59:13 -0500 Subject: [PATCH 078/110] Use proxy in challenge test --- solgen/src/mocks/SimpleProxy.sol | 20 ++++++++++++++++ system_tests/full_challenge_test.go | 36 +++++++++++------------------ 2 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 solgen/src/mocks/SimpleProxy.sol diff --git a/solgen/src/mocks/SimpleProxy.sol b/solgen/src/mocks/SimpleProxy.sol new file mode 100644 index 0000000000..2e1b599dab --- /dev/null +++ b/solgen/src/mocks/SimpleProxy.sol @@ -0,0 +1,20 @@ +// +// Copyright 2022, Offchain Labs, Inc. All rights reserved. +// SPDX-License-Identifier: UNLICENSED +// + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/proxy/Proxy.sol"; + +contract SimpleProxy is Proxy { + address private immutable impl; + + constructor(address impl_) { + impl = impl_; + } + + function _implementation() internal view override returns (address) { + return impl; + } +} diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 254a543746..7a14272bc2 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -77,27 +77,21 @@ func CreateChallenge( challenger common.Address, ) (*mocksgen.MockResultReceiver, common.Address) { resultReceiverAddr, _, resultReceiver, err := mocksgen.DeployMockResultReceiver(auth, client) - if err != nil { - t.Fatal(err) - } - - challengeManagerAddr, tx, challengeManager, err := challengegen.DeployChallengeManager(auth, client) - if err != nil { - t.Fatal(err) - } + Require(t, err) + challengeManagerLogic, tx, _, err := challengegen.DeployChallengeManager(auth, client) + Require(t, err) _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) - if err != nil { - t.Fatal(err) - } + Require(t, err) + challengeManagerAddr, tx, _, err := mocksgen.DeploySimpleProxy(auth, client, challengeManagerLogic) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) + Require(t, err) + challengeManager, err := challengegen.NewChallengeManager(challengeManagerAddr, client) + Require(t, err) tx, err = challengeManager.Initialize(auth, resultReceiverAddr, sequencerInbox, delayedBridge, ospEntry) - if err != nil { - t.Fatal(err) - } + Require(t, err) _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) - if err != nil { - t.Fatal(err) - } - + Require(t, err) tx, err = challengeManager.CreateChallenge( auth, wasmModuleRoot, @@ -115,11 +109,9 @@ func CreateChallenge( big.NewInt(100000), big.NewInt(100000), ) + Require(t, err) _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) - if err != nil { - t.Fatal(err) - } - + Require(t, err) return resultReceiver, challengeManagerAddr } From 2f49ee4ab3d56506afd8cb44dd5dcaf792447883 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 16:14:20 -0500 Subject: [PATCH 079/110] Create challenge through mock receiver --- solgen/src/mocks/MockResultReceiver.sol | 30 ++++++++++++++++++++++++- system_tests/full_challenge_test.go | 19 +++++++++++----- validator/challenge_test.go | 2 +- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/solgen/src/mocks/MockResultReceiver.sol b/solgen/src/mocks/MockResultReceiver.sol index 92ca92948c..a7aabbe7d5 100644 --- a/solgen/src/mocks/MockResultReceiver.sol +++ b/solgen/src/mocks/MockResultReceiver.sol @@ -2,13 +2,41 @@ pragma solidity ^0.8.0; import "../challenge/IChallengeResultReceiver.sol"; +import "../challenge/IChallengeManager.sol"; contract MockResultReceiver is IChallengeResultReceiver { + IChallengeManager manager; address public winner; address public loser; uint256 public challengeIndex; -event ChallengeCompleted(uint256 indexed challengeIndex, address indexed winner, address indexed loser); + event ChallengeCompleted(uint256 indexed challengeIndex, address indexed winner, address indexed loser); + + constructor (IChallengeManager manager_) { + manager = manager_; + } + + function createChallenge( + bytes32 wasmModuleRoot_, + MachineStatus[2] calldata startAndEndMachineStatuses_, + GlobalState[2] calldata startAndEndGlobalStates_, + uint64 numBlocks, + address asserter_, + address challenger_, + uint256 asserterTimeLeft_, + uint256 challengerTimeLeft_ + ) external returns (uint64) { + return manager.createChallenge( + wasmModuleRoot_, + startAndEndMachineStatuses_, + startAndEndGlobalStates_, + numBlocks, + asserter_, + challenger_, + asserterTimeLeft_, + challengerTimeLeft_ + ); + } function completeChallenge(uint256 challengeIndex_, address winner_, address loser_) external override { winner = winner_; diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 7a14272bc2..2fff8b6af7 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -76,8 +76,6 @@ func CreateChallenge( asserter common.Address, challenger common.Address, ) (*mocksgen.MockResultReceiver, common.Address) { - resultReceiverAddr, _, resultReceiver, err := mocksgen.DeployMockResultReceiver(auth, client) - Require(t, err) challengeManagerLogic, tx, _, err := challengegen.DeployChallengeManager(auth, client) Require(t, err) _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) @@ -88,20 +86,29 @@ func CreateChallenge( Require(t, err) challengeManager, err := challengegen.NewChallengeManager(challengeManagerAddr, client) Require(t, err) + + resultReceiverAddr, _, resultReceiver, err := mocksgen.DeployMockResultReceiver(auth, client, challengeManagerAddr) + Require(t, err) tx, err = challengeManager.Initialize(auth, resultReceiverAddr, sequencerInbox, delayedBridge, ospEntry) Require(t, err) _, err = arbutil.EnsureTxSucceeded(context.Background(), client, tx) Require(t, err) - tx, err = challengeManager.CreateChallenge( + tx, err = resultReceiver.CreateChallenge( auth, wasmModuleRoot, [2]uint8{ validator.StatusFinished, validator.StatusFinished, }, - [2]challengegen.GlobalState{ - startGlobalState.AsSolidityStruct(), - endGlobalState.AsSolidityStruct(), + [2]mocksgen.GlobalState{ + { + Bytes32Vals: [2][32]byte{startGlobalState.BlockHash, startGlobalState.SendRoot}, + U64Vals: [2]uint64{startGlobalState.Batch, startGlobalState.PosInBatch}, + }, + { + Bytes32Vals: [2][32]byte{endGlobalState.BlockHash, endGlobalState.SendRoot}, + U64Vals: [2]uint64{endGlobalState.Batch, endGlobalState.PosInBatch}, + }, }, numBlocks, asserter, diff --git a/validator/challenge_test.go b/validator/challenge_test.go index 59b1a91d3f..cc27074ee1 100644 --- a/validator/challenge_test.go +++ b/validator/challenge_test.go @@ -52,7 +52,7 @@ func CreateChallenge( asserter common.Address, challenger common.Address, ) (*mocksgen.MockResultReceiver, common.Address) { - resultReceiverAddr, _, resultReceiver, err := mocksgen.DeployMockResultReceiver(auth, client) + resultReceiverAddr, _, resultReceiver, err := mocksgen.DeployMockResultReceiver(auth, client, common.Address{}) Require(t, err) var startHashBytes [32]byte From 171572f5a66d2954aa9bf2bbbd3cd0248522910f Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 16:19:04 -0500 Subject: [PATCH 080/110] Fix index in full challenge text --- system_tests/full_challenge_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 2fff8b6af7..741f24a366 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -330,12 +330,12 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { ) confirmLatestBlock(ctx, t, l1Info, l1Backend) - asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challengeManagerAddr, 0, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) + asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challengeManagerAddr, 1, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } - challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challengeManagerAddr, 0, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) + challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challengeManagerAddr, 1, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) if err != nil { t.Fatal(err) } From 7e479a9d12cb282ed1a5d018429aa51b169131f2 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 16:24:07 -0500 Subject: [PATCH 081/110] No confirmation blocks in full challenge test --- system_tests/full_challenge_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index 741f24a366..e7fd57991c 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -330,12 +330,12 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { ) confirmLatestBlock(ctx, t, l1Info, l1Backend) - asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challengeManagerAddr, 1, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 12) + asserterManager, err := validator.NewChallengeManager(ctx, l1Backend, &asserterTxOpts, asserterTxOpts.From, challengeManagerAddr, 1, asserterL2Blockchain, asserterL2.InboxReader, asserterL2.InboxTracker, asserterL2.TxStreamer, 0, 4, 0) if err != nil { t.Fatal(err) } - challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challengeManagerAddr, 1, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 12) + challengerManager, err := validator.NewChallengeManager(ctx, l1Backend, &challengerTxOpts, challengerTxOpts.From, challengeManagerAddr, 1, challengerL2Blockchain, challengerL2.InboxReader, challengerL2.InboxTracker, challengerL2.TxStreamer, 0, 4, 0) if err != nil { t.Fatal(err) } From 5cdabb03c1dc86b365ae82eabddb76846e2c359a Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 16:31:31 -0500 Subject: [PATCH 082/110] Fix initiatedChallengeID --- validator/challenge_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index 2be3fc4df1..b13be49d76 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -34,7 +34,7 @@ func init() { if err != nil { panic(err) } - initiatedChallengeID = parsedChallengeManagerABI.Events["ChallengeInitiated"].ID + initiatedChallengeID = parsedChallengeManagerABI.Events["InitiatedChallenge"].ID challengeBisectedID = parsedChallengeManagerABI.Events["Bisected"].ID executionChallengeBegunID = parsedChallengeManagerABI.Events["ExecutionChallengeBegun"].ID } From 81177b3871f8c58196c7664b5ad1b219b551cd54 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 17:38:54 -0500 Subject: [PATCH 083/110] Fix typescript one step proof test --- solgen/deploy/SequencerInboxStubCreator.js | 4 ++-- solgen/test/one-step-proof.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/solgen/deploy/SequencerInboxStubCreator.js b/solgen/deploy/SequencerInboxStubCreator.js index 20a84aa307..b5dd2f1bb9 100644 --- a/solgen/deploy/SequencerInboxStubCreator.js +++ b/solgen/deploy/SequencerInboxStubCreator.js @@ -4,8 +4,8 @@ module.exports = async (hre) => { const { deployer } = await getNamedAccounts(); const bridge = await ethers.getContract("BridgeStub"); - - await deploy("SequencerInboxStub", {from: deployer, args: [bridge.address, deployer]}) + const maxTime = {delayBlocks: 10000, futureBlocks: 10000, delaySeconds: 10000, futureSeconds: 10000} + await deploy("SequencerInboxStub", {from: deployer, args: [bridge.address, deployer, maxTime]}) }; module.exports.tags = ["SequencerInboxStub", "test"]; diff --git a/solgen/test/one-step-proof.ts b/solgen/test/one-step-proof.ts index caf8f37721..f842125612 100644 --- a/solgen/test/one-step-proof.ts +++ b/solgen/test/one-step-proof.ts @@ -15,7 +15,7 @@ async function sendTestMessages() { const path = msgRoot + "msg" + String(msgNum) + ".bin"; const buf = fs.readFileSync(path); await inbox.sendL2MessageFromOrigin(buf, gasOpts); - await seqInbox.addSequencerL2BatchFromOrigin(msgNum, buf, 0, ethers.constants.AddressZero, gasOpts); + await seqInbox.addSequencerL2BatchFromOrigin(1 + msgNum, buf, 0, ethers.constants.AddressZero, gasOpts); } } @@ -30,6 +30,8 @@ describe("OneStepProof", function () { await sendTestMessages(); }) + it("should deploy test harness", function() {}) + for (let file of dir) { if (!file.endsWith(".json")) continue; it("Should pass " + file + " proofs", async function () { From 89514cdc00ef1b5d52483e8ed310ca82efa38ec9 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Mon, 21 Feb 2022 17:07:38 -0600 Subject: [PATCH 084/110] remove tip in excess of basefee logic --- arbos/tx_processor.go | 20 ++++----- go-ethereum | 2 +- system_tests/common_test.go | 7 +++ system_tests/fees_test.go | 88 +++++++++++++++++++++++++++++++++++++ system_tests/test_info.go | 8 ++++ 5 files changed, 112 insertions(+), 13 deletions(-) create mode 100644 system_tests/fees_test.go diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 241d2fe7de..acab026614 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -37,8 +37,6 @@ type TxProcessor struct { state *arbosState.ArbosState PosterFee *big.Int // set once in GasChargingHook to track L1 calldata costs posterGas uint64 - tipWeiPerGas *big.Int - tipRecipient common.Address computeHoldGas uint64 // amount of gas temporarily held to prevent compute from exceeding the gas limit Callers []common.Address TopTxType *byte // set once in StartTxHook @@ -52,17 +50,15 @@ func NewTxProcessor(evm *vm.EVM, msg core.Message) *TxProcessor { panic(err) } arbosState.SetLastTimestampSeen(evm.Context.Time.Uint64()) - networkFeeAccount, err := arbosState.NetworkFeeAccount() + /*networkFeeAccount, err := arbosState.NetworkFeeAccount() if err != nil { panic(err) - } + }*/ return &TxProcessor{ msg: msg, state: arbosState, PosterFee: new(big.Int), posterGas: 0, - tipWeiPerGas: new(big.Int), - tipRecipient: networkFeeAccount, Callers: []common.Address{}, TopTxType: nil, evm: evm, @@ -223,7 +219,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r return false, 0, nil, nil } -func (p *TxProcessor) GasChargingHook(gasRemaining *uint64, txGasPrice *big.Int) error { +func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { // Because a user pays a 1-dimensional gas price, we must re-express poster L1 calldata costs // as if the user was buying an equivalent amount of L2 compute gas. This hook determines what // that cost looks like, ensuring the user can pay and saving the result for later reference. @@ -234,7 +230,7 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64, txGasPrice *big.Int) gasPrice := p.evm.Context.BaseFee l1Pricing := p.state.L1PricingState() aggregator := p.getReimbursableAggregator() - posterCost, reimbursable, err := l1Pricing.PosterDataCost(from, aggregator, p.msg.Data()) + posterCost, _, err := l1Pricing.PosterDataCost(from, aggregator, p.msg.Data()) p.state.Restrict(err) if p.msg.RunMode() == types.MessageGasEstimationMode { @@ -262,7 +258,7 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64, txGasPrice *big.Int) p.PosterFee = new(big.Int).Mul(posterCostInL2Gas, gasPrice) // round down gasNeededToStartEVM = p.posterGas - // Most users shouldn't set a tip, but if one is specified ensure the user has enough funds to + /*// Most users shouldn't set a tip, but if one is specified ensure the user has enough funds to // tip the appropriate party (the poster, if reimbursable, otherwise the network fee account) if txGasPrice != nil && util.BigGreaterThan(txGasPrice, gasPrice) { p.tipWeiPerGas = util.BigSub(txGasPrice, gasPrice) @@ -274,7 +270,7 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64, txGasPrice *big.Int) if util.BigLessThan(p.evm.StateDB.GetBalance(from), tip) { return core.ErrInsufficientFunds } - p.evm.StateDB.SubBalance(from, tip) + p.evm.StateDB.SubBalance(from, tip)*/ } if *gasRemaining < gasNeededToStartEVM { @@ -366,8 +362,8 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { p.evm.StateDB.AddBalance(networkFeeAccount, computeCost) p.evm.StateDB.AddBalance(p.evm.Context.Coinbase, p.PosterFee) - p.evm.StateDB.AddBalance(p.tipRecipient, util.BigMul(p.tipWeiPerGas, gasUsed)) // tip on gas used - p.evm.StateDB.AddBalance(p.msg.From(), util.BigMulByUint(p.tipWeiPerGas, gasLeft)) // refund unused tip + /*p.evm.StateDB.AddBalance(p.tipRecipient, util.BigMul(p.tipWeiPerGas, gasUsed)) // tip on gas used + p.evm.StateDB.AddBalance(p.msg.From(), util.BigMulByUint(p.tipWeiPerGas, gasLeft)) // refund unused tip*/ if p.msg.GasPrice().Sign() > 0 { // in tests, gas price coud be 0 // ArbOS's gas pool is meant to enforce the computational speed-limit. diff --git a/go-ethereum b/go-ethereum index b943b1b07f..b0f9c1b40f 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b943b1b07fabb79a11f88024b86aca8fb6a32b20 +Subproject commit b0f9c1b40f48c3c6a121df6a3e7523c02674a418 diff --git a/system_tests/common_test.go b/system_tests/common_test.go index 04708b87e0..2fd04f3bc5 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -253,3 +253,10 @@ func Create2ndNodeWithConfig(t *testing.T, ctx context.Context, first *arbnode.N l2client := ClientForArbBackend(t, node.Backend) return l2client, node } + +func GetBalance(t *testing.T, ctx context.Context, client *ethclient.Client, account common.Address) *big.Int { + t.Helper() + balance, err := client.BalanceAt(ctx, account, nil) + Require(t, err, "could not get balance") + return balance +} diff --git a/system_tests/fees_test.go b/system_tests/fees_test.go new file mode 100644 index 0000000000..b26ffb82df --- /dev/null +++ b/system_tests/fees_test.go @@ -0,0 +1,88 @@ +// +// Copyright 2021, Offchain Labs, Inc. All rights reserved. +// + +package arbtest + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/solgen/go/mocksgen" + "github.com/offchainlabs/arbstate/solgen/go/precompilesgen" + "github.com/offchainlabs/arbstate/util" + "github.com/offchainlabs/arbstate/util/colors" + "github.com/offchainlabs/arbstate/util/testhelpers" +) + +func TestTips(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + l2info, _, client := CreateTestL2(t, ctx) + auth := l2info.GetDefaultTransactOpts("Owner") + callOpts := l2info.GetDefaultCallOpts("Owner") + aggregator := testhelpers.RandomAddress() + + // set a preferred aggregator who won't be the one to post the tx + arbAggregator, err := precompilesgen.NewArbAggregator(common.HexToAddress("0x6d"), client) + Require(t, err, "could not deploy ArbAggregator contract") + tx, err := arbAggregator.SetPreferredAggregator(&auth, aggregator) + Require(t, err, "could not set L2 gas price") + _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + // get the network fee account + arbOwner, err := precompilesgen.NewArbOwner(common.HexToAddress("0x70"), client) + Require(t, err, "could not deploy ArbOwner contract") + networkFeeAccount, err := arbOwner.GetNetworkFeeAccount(callOpts) + Require(t, err, "could not get the network fee account") + + networkBefore := GetBalance(t, ctx, client, networkFeeAccount) + colors.PrintMint("network: ", networkFeeAccount, networkBefore) + + auth.GasFeeCap = util.BigMulByUfrac(l2info.GasPrice, 5, 4) // add room for a 20% tip + auth.GasTipCap = util.BigMulByUfrac(l2info.GasPrice, 1, 4) // add a 20% tip + + _, tx, _, err = mocksgen.DeploySimple(&auth, client) + Require(t, err, "could not deploy contract") + _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + networkAfter := GetBalance(t, ctx, client, networkFeeAccount) + colors.PrintMint("network: ", networkFeeAccount, networkAfter) + + colors.PrintBlue("pricing: ", l2info.GasPrice, auth.GasFeeCap, auth.GasTipCap) + colors.PrintBlue("payment: ", tx.GasPrice(), tx.GasFeeCap(), tx.GasTipCap()) + + if !util.BigEquals(tx.GasPrice(), auth.GasFeeCap) { + Fail(t, "user did not pay the tip") + } + +} + +// Test that the sequencer won't subvert a user's aggregation preferences +func TestSequencerWontPostWhenNotPreferred(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + l2info, _, client := CreateTestL2(t, ctx) + auth := l2info.GetDefaultTransactOpts("Owner") + + // prefer a 3rd party aggregator + arbAggregator, err := precompilesgen.NewArbAggregator(common.HexToAddress("0x6d"), client) + Require(t, err, "could not deploy ArbAggregator contract") + tx, err := arbAggregator.SetPreferredAggregator(&auth, testhelpers.RandomAddress()) + Require(t, err, "could not set L2 gas price") + _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + Require(t, err) + + // get the network fee account + _, err = arbAggregator.SetPreferredAggregator(&auth, testhelpers.RandomAddress()) + colors.PrintBlue("expecting error: ", err) + if err == nil { + Fail(t, "the sequencer should have rejected this tx") + } +} diff --git a/system_tests/test_info.go b/system_tests/test_info.go index 2f0dc506a5..5be0eb061b 100644 --- a/system_tests/test_info.go +++ b/system_tests/test_info.go @@ -168,6 +168,14 @@ func (b *BlockchainTestInfo) GetDefaultTransactOpts(name string) bind.TransactOp } } +func (b *BlockchainTestInfo) GetDefaultCallOpts(name string) *bind.CallOpts { + b.T.Helper() + auth := b.GetDefaultTransactOpts(name) + return &bind.CallOpts{ + From: auth.From, + } +} + func (b *BlockchainTestInfo) SignTxAs(name string, data types.TxData) *types.Transaction { b.T.Helper() info := b.GetInfoWithPrivKey(name) From 546c11125dc8afa6b38595ce219301d5ed81aacb Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Mon, 21 Feb 2022 17:33:36 -0600 Subject: [PATCH 085/110] add an activation day to precompiles and their methods --- precompiles/ArbAddressTable_test.go | 1 + precompiles/precompile.go | 32 +++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/precompiles/ArbAddressTable_test.go b/precompiles/ArbAddressTable_test.go index d71286f29e..a07783faab 100644 --- a/precompiles/ArbAddressTable_test.go +++ b/precompiles/ArbAddressTable_test.go @@ -156,6 +156,7 @@ func newMockEVMForTesting() *vm.EVM { context := vm.BlockContext{ BlockNumber: big.NewInt(0), GasLimit: ^uint64(0), + Time: big.NewInt(0), } evm := vm.NewEVM(context, vm.TxContext{}, statedb, chainConfig, vm.Config{}) evm.ProcessingHook = &arbos.TxProcessor{} diff --git a/precompiles/precompile.go b/precompiles/precompile.go index 615688498d..52f062d932 100644 --- a/precompiles/precompile.go +++ b/precompiles/precompile.go @@ -57,18 +57,20 @@ const ( ) type Precompile struct { - methods map[[4]byte]PrecompileMethod - events map[string]PrecompileEvent - implementer reflect.Value - address common.Address + methods map[[4]byte]PrecompileMethod + events map[string]PrecompileEvent + implementer reflect.Value + address common.Address + activationTime *big.Int } type PrecompileMethod struct { - name string - template abi.Method - purity purity - handler reflect.Method - implementer reflect.Value + name string + template abi.Method + purity purity + handler reflect.Method + implementer reflect.Value + activationTime *big.Int } type PrecompileEvent struct { @@ -167,6 +169,7 @@ func makePrecompile(metadata *bind.MetaData, implementer interface{}) (addr, Arb purity, handler, reflect.ValueOf(implementer), + big.NewInt(0), } } @@ -376,6 +379,7 @@ func makePrecompile(metadata *bind.MetaData, implementer interface{}) (addr, Arb events, reflect.ValueOf(implementer), address, + big.NewInt(0), } } @@ -456,14 +460,20 @@ func (p Precompile) Call( evm *vm.EVM, ) (output []byte, gasLeft uint64, err error) { + timestamp := evm.Context.Time + if util.BigLessThan(timestamp, p.activationTime) { + // the precompile isn't yet active, so treat this call as if it were to a contract that doesn't exist + return []byte{}, gasSupplied, nil + } + if len(input) < 4 { // ArbOS precompiles always have canonical method selectors return nil, 0, vm.ErrExecutionReverted } id := *(*[4]byte)(input) method, ok := p.methods[id] - if !ok { - // method does not exist + if !ok || util.BigLessThan(timestamp, method.activationTime) { + // method does not exist or hasn't yet been activated return nil, 0, vm.ErrExecutionReverted } From 90e2c5121395c61a59b0d7aae0e5add243ff6b63 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Mon, 21 Feb 2022 20:12:10 -0500 Subject: [PATCH 086/110] Fix hostio prover test --- Makefile | 6 +- solgen/src/bridge/SequencerInbox.sol | 2 +- solgen/src/mocks/SequencerInboxStub.sol | 6 ++ solgen/test/one-step-proof.ts | 2 +- system_tests/full_challenge_test.go | 80 ++++++++++--------------- 5 files changed, 43 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index 9066966fb8..8aa8a3e2bb 100644 --- a/Makefile +++ b/Makefile @@ -235,9 +235,6 @@ $(output_root)/lib/host_io.wasm: arbitrator/wasm-libraries/host-io/src/** arbitrator/prover/test-cases/%.wasm: arbitrator/prover/test-cases/%.wat wat2wasm $< -o $@ -solgen/test/proofs/%.json: arbitrator/prover/test-cases/%.wasm $(arbitrator_prover_bin) - $(arbitrator_prover_bin) $< -o $@ --allow-hostapi --always-merkleize - solgen/test/proofs/float%.json: arbitrator/prover/test-cases/float%.wasm $(arbitrator_prover_bin) $(output_root)/lib/soft-float.wasm $(arbitrator_prover_bin) $< -l $(output_root)/lib/soft-float.wasm -o $@ -b --allow-hostapi --require-success --always-merkleize @@ -247,6 +244,9 @@ solgen/test/proofs/rust-%.json: arbitrator/prover/test-cases/rust/target/wasm32- solgen/test/proofs/go.json: arbitrator/prover/test-cases/go/main $(arbitrator_prover_bin) $(arbitrator_wasm_libs) $(arbitrator_prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -i 5000000 +solgen/test/proofs/%.json: arbitrator/prover/test-cases/%.wasm $(arbitrator_prover_bin) + $(arbitrator_prover_bin) $< -o $@ --allow-hostapi --always-merkleize + # strategic rules to minimize dependency building .make/lint: build-node-deps | .make diff --git a/solgen/src/bridge/SequencerInbox.sol b/solgen/src/bridge/SequencerInbox.sol index 54818aa1cf..74fdffbee4 100644 --- a/solgen/src/bridge/SequencerInbox.sol +++ b/solgen/src/bridge/SequencerInbox.sol @@ -45,7 +45,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox maxTimeVariation = maxTimeVariation_; } - function getTimeBounds() internal view returns (TimeBounds memory) { + function getTimeBounds() internal view virtual returns (TimeBounds memory) { TimeBounds memory bounds; if (block.timestamp > maxTimeVariation.delaySeconds) { bounds.minTimestamp = uint64(block.timestamp - maxTimeVariation.delaySeconds); diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index 9f70d84415..3f63ffdf5c 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -13,7 +13,9 @@ contract SequencerInboxStub is SequencerInbox { rollup = msg.sender; maxTimeVariation = maxTimeVariation_; isBatchPoster[sequencer_] = true; + } + function addInitMessage() external { ( bytes32 dataHash, TimeBounds memory timeBounds @@ -33,4 +35,8 @@ contract SequencerInboxStub is SequencerInbox { BatchDataLocation.NoData ); } + + function getTimeBounds() internal view override returns (TimeBounds memory bounds) { + return bounds; + } } diff --git a/solgen/test/one-step-proof.ts b/solgen/test/one-step-proof.ts index f842125612..c0c744fb00 100644 --- a/solgen/test/one-step-proof.ts +++ b/solgen/test/one-step-proof.ts @@ -15,7 +15,7 @@ async function sendTestMessages() { const path = msgRoot + "msg" + String(msgNum) + ".bin"; const buf = fs.readFileSync(path); await inbox.sendL2MessageFromOrigin(buf, gasOpts); - await seqInbox.addSequencerL2BatchFromOrigin(1 + msgNum, buf, 0, ethers.constants.AddressZero, gasOpts); + await seqInbox.addSequencerL2BatchFromOrigin(msgNum, buf, 0, ethers.constants.AddressZero, gasOpts); } } diff --git a/system_tests/full_challenge_test.go b/system_tests/full_challenge_test.go index e7fd57991c..05ac6c734b 100644 --- a/system_tests/full_challenge_test.go +++ b/system_tests/full_challenge_test.go @@ -146,43 +146,27 @@ func makeBatch(t *testing.T, l2Node *arbnode.Node, l2Info *BlockchainTestInfo, b value++ } err := writeTxToBatch(batchWriter, l2Info.PrepareTx("Owner", "Destination", 1000000, big.NewInt(value), []byte{})) - if err != nil { - t.Fatal(err) - } + Require(t, err) } err := batchWriter.Flush() - if err != nil { - t.Fatal(err) - } + Require(t, err) tx, err := seqInbox.AddSequencerL2BatchFromOrigin(sequencer, big.NewInt(1), batchBuffer.Bytes(), big.NewInt(0), common.Address{}) - if err != nil { - t.Fatal(err) - } + Require(t, err) receipt, err := arbutil.EnsureTxSucceeded(ctx, backend, tx) - if err != nil { - t.Fatal(err) - } + Require(t, err) nodeSeqInbox, err := arbnode.NewSequencerInbox(backend, seqInboxAddr, 0) - if err != nil { - t.Fatal(err) - } + Require(t, err) batches, err := nodeSeqInbox.LookupBatchesInRange(ctx, receipt.BlockNumber, receipt.BlockNumber) - if err != nil { - t.Fatal(err) - } + Require(t, err) if len(batches) == 0 { t.Fatal("batch not found after AddSequencerL2BatchFromOrigin") } err = l2Node.InboxTracker.AddSequencerBatches(ctx, backend, batches) - if err != nil { - t.Fatal(err) - } + Require(t, err) _, err = l2Node.InboxTracker.GetBatchMetadata(0) - if err != nil { - t.Fatal("failed to get batch metadata after adding batch:", err) - } + Require(t, err, "failed to get batch metadata after adding batch:") } func confirmLatestBlock(ctx context.Context, t *testing.T, l1Info *BlockchainTestInfo, backend arbutil.L1Interface) { @@ -219,10 +203,10 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { sequencerTxOpts := l1Info.GetDefaultTransactOpts("sequencer") asserterTxOpts := l1Info.GetDefaultTransactOpts("asserter") challengerTxOpts := l1Info.GetDefaultTransactOpts("challenger") - delayedBridge, _, _, err := mocksgen.DeployBridgeStub(&deployerTxOpts, l1Backend) - if err != nil { - t.Fatal(err) - } + delayedBridge, tx, _, err := mocksgen.DeployBridgeStub(&deployerTxOpts, l1Backend) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), l1Backend, tx) + Require(t, err) timeBounds := mocksgen.ISequencerInboxMaxTimeVariation{ DelayBlocks: big.NewInt(10000), @@ -230,48 +214,48 @@ func runChallengeTest(t *testing.T, asserterIsCorrect bool) { DelaySeconds: big.NewInt(10000), FutureSeconds: big.NewInt(10000), } - asserterSeqInboxAddr, _, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub( + asserterSeqInboxAddr, tx, asserterSeqInbox, err := mocksgen.DeploySequencerInboxStub( &deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer"), timeBounds, ) - if err != nil { - t.Fatal(err) - } - challengerSeqInboxAddr, _, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub( + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), l1Backend, tx) + Require(t, err) + tx, err = asserterSeqInbox.AddInitMessage(&deployerTxOpts) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), l1Backend, tx) + Require(t, err) + challengerSeqInboxAddr, tx, challengerSeqInbox, err := mocksgen.DeploySequencerInboxStub( &deployerTxOpts, l1Backend, delayedBridge, l1Info.GetAddress("sequencer"), timeBounds, ) - if err != nil { - t.Fatal(err) - } + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), l1Backend, tx) + Require(t, err) + tx, err = challengerSeqInbox.AddInitMessage(&deployerTxOpts) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(context.Background(), l1Backend, tx) + Require(t, err) asserterL2Info, asserterL2Stack, asserterL2ChainDb, asserterL2Blockchain := createL2BlockChain(t, nil) rollupAddresses.SequencerInbox = asserterSeqInboxAddr asserterL2, err := arbnode.CreateNode(asserterL2Stack, asserterL2ChainDb, &conf, asserterL2Blockchain, l1Backend, rollupAddresses, nil) - if err != nil { - t.Fatal(err) - } + Require(t, err) err = asserterL2.Start(ctx) - if err != nil { - t.Fatal(err) - } + Require(t, err) challengerL2Info, challengerL2Stack, challengerL2ChainDb, challengerL2Blockchain := createL2BlockChain(t, nil) rollupAddresses.SequencerInbox = challengerSeqInboxAddr challengerL2, err := arbnode.CreateNode(challengerL2Stack, challengerL2ChainDb, &conf, challengerL2Blockchain, l1Backend, rollupAddresses, nil) - if err != nil { - t.Fatal(err) - } + Require(t, err) err = challengerL2.Start(ctx) - if err != nil { - t.Fatal(err) - } + Require(t, err) asserterL2Info.GenerateAccount("Destination") challengerL2Info.SetFullAccountInfo("Destination", asserterL2Info.GetInfoWithPrivKey("Destination")) From 3b6283199fd5248879bcec2d9ee0aea379ea2ba5 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Mon, 21 Feb 2022 20:56:20 -0600 Subject: [PATCH 087/110] use arbos versions as flag days --- arbos/arbosState/arbosstate.go | 10 ++++++ .../{test_common.go => common_test.go} | 4 +++ precompiles/ArbSys.go | 2 +- precompiles/precompile.go | 33 ++++++++++--------- 4 files changed, 32 insertions(+), 17 deletions(-) rename arbos/arbosState/{test_common.go => common_test.go} (81%) diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 465802a20e..d406eaec62 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -105,6 +105,16 @@ func NewArbosMemoryBackedArbOSState() (*ArbosState, *state.StateDB) { return state, statedb } +// Get the ArbOS version +func ArbOSVersion(stateDB vm.StateDB) uint64 { + backingStorage := storage.NewGeth(stateDB, burn.NewSystemBurner(false)) + arbosVersion, err := backingStorage.GetUint64ByUint64(uint64(versionOffset)) + if err != nil { + log.Fatal("faled to get the ArbOS version", err) + } + return arbosVersion +} + type ArbosStateOffset uint64 const ( diff --git a/arbos/arbosState/test_common.go b/arbos/arbosState/common_test.go similarity index 81% rename from arbos/arbosState/test_common.go rename to arbos/arbosState/common_test.go index 051f6fca06..61d0a7e637 100644 --- a/arbos/arbosState/test_common.go +++ b/arbos/arbosState/common_test.go @@ -1,3 +1,7 @@ +// +// Copyright 2021-2022, Offchain Labs, Inc. All rights reserved. +// + package arbosState import ( diff --git a/precompiles/ArbSys.go b/precompiles/ArbSys.go index 1d2d924ee9..5a2a694fa6 100644 --- a/precompiles/ArbSys.go +++ b/precompiles/ArbSys.go @@ -53,7 +53,7 @@ func (con *ArbSys) ArbChainID(c ctx, evm mech) (huge, error) { // Gets the current ArbOS version func (con *ArbSys) ArbOSVersion(c ctx, evm mech) (huge, error) { - version := new(big.Int).SetUint64(52 + c.state.FormatVersion()) // Nitro starts at version 53 + version := new(big.Int).SetUint64(55 + c.state.FormatVersion()) // Nitro starts at version 56 return version, nil } diff --git a/precompiles/precompile.go b/precompiles/precompile.go index 52f062d932..e064638962 100644 --- a/precompiles/precompile.go +++ b/precompiles/precompile.go @@ -57,20 +57,20 @@ const ( ) type Precompile struct { - methods map[[4]byte]PrecompileMethod - events map[string]PrecompileEvent - implementer reflect.Value - address common.Address - activationTime *big.Int + methods map[[4]byte]PrecompileMethod + events map[string]PrecompileEvent + implementer reflect.Value + address common.Address + arbosVersion uint64 } type PrecompileMethod struct { - name string - template abi.Method - purity purity - handler reflect.Method - implementer reflect.Value - activationTime *big.Int + name string + template abi.Method + purity purity + handler reflect.Method + implementer reflect.Value + arbosVersion uint64 } type PrecompileEvent struct { @@ -169,7 +169,7 @@ func makePrecompile(metadata *bind.MetaData, implementer interface{}) (addr, Arb purity, handler, reflect.ValueOf(implementer), - big.NewInt(0), + 0, } } @@ -379,7 +379,7 @@ func makePrecompile(metadata *bind.MetaData, implementer interface{}) (addr, Arb events, reflect.ValueOf(implementer), address, - big.NewInt(0), + 0, } } @@ -460,8 +460,9 @@ func (p Precompile) Call( evm *vm.EVM, ) (output []byte, gasLeft uint64, err error) { - timestamp := evm.Context.Time - if util.BigLessThan(timestamp, p.activationTime) { + arbosVersion := arbosState.ArbOSVersion(evm.StateDB) + + if arbosVersion < p.arbosVersion { // the precompile isn't yet active, so treat this call as if it were to a contract that doesn't exist return []byte{}, gasSupplied, nil } @@ -472,7 +473,7 @@ func (p Precompile) Call( } id := *(*[4]byte)(input) method, ok := p.methods[id] - if !ok || util.BigLessThan(timestamp, method.activationTime) { + if !ok || arbosVersion < method.arbosVersion { // method does not exist or hasn't yet been activated return nil, 0, vm.ErrExecutionReverted } From f2d7d0a133277d2929e144199e5902e9d568a5ba Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Mon, 21 Feb 2022 22:08:41 -0600 Subject: [PATCH 088/110] let GasChargingHook() decide the tip recipient --- arbos/tx_processor.go | 31 ++++++++++--------------------- go-ethereum | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index acab026614..4dd90b09a9 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -50,10 +50,6 @@ func NewTxProcessor(evm *vm.EVM, msg core.Message) *TxProcessor { panic(err) } arbosState.SetLastTimestampSeen(evm.Context.Time.Uint64()) - /*networkFeeAccount, err := arbosState.NetworkFeeAccount() - if err != nil { - panic(err) - }*/ return &TxProcessor{ msg: msg, state: arbosState, @@ -219,7 +215,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r return false, 0, nil, nil } -func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { +func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) (*common.Address, error) { // Because a user pays a 1-dimensional gas price, we must re-express poster L1 calldata costs // as if the user was buying an equivalent amount of L2 compute gas. This hook determines what // that cost looks like, ensuring the user can pay and saving the result for later reference. @@ -230,7 +226,7 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { gasPrice := p.evm.Context.BaseFee l1Pricing := p.state.L1PricingState() aggregator := p.getReimbursableAggregator() - posterCost, _, err := l1Pricing.PosterDataCost(from, aggregator, p.msg.Data()) + posterCost, reimburse, err := l1Pricing.PosterDataCost(from, aggregator, p.msg.Data()) p.state.Restrict(err) if p.msg.RunMode() == types.MessageGasEstimationMode { @@ -257,25 +253,18 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { p.posterGas = posterCostInL2Gas.Uint64() p.PosterFee = new(big.Int).Mul(posterCostInL2Gas, gasPrice) // round down gasNeededToStartEVM = p.posterGas + } - /*// Most users shouldn't set a tip, but if one is specified ensure the user has enough funds to - // tip the appropriate party (the poster, if reimbursable, otherwise the network fee account) - if txGasPrice != nil && util.BigGreaterThan(txGasPrice, gasPrice) { - p.tipWeiPerGas = util.BigSub(txGasPrice, gasPrice) - } - if reimbursable && aggregator != nil { - p.tipRecipient = *aggregator - } - tip := util.BigMulByUint(p.tipWeiPerGas, p.msg.Gas()) - if util.BigLessThan(p.evm.StateDB.GetBalance(from), tip) { - return core.ErrInsufficientFunds - } - p.evm.StateDB.SubBalance(from, tip)*/ + // Most users shouldn't set a tip, but if specified only give it to the poster if they're reimbursable + tipRecipient := aggregator + if !reimburse { + networkFeeAccount, _ := p.state.NetworkFeeAccount() + tipRecipient = &networkFeeAccount } if *gasRemaining < gasNeededToStartEVM { // the user couldn't pay for call data, so give up - return core.ErrIntrinsicGas + return tipRecipient, core.ErrIntrinsicGas } *gasRemaining -= gasNeededToStartEVM @@ -289,7 +278,7 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) error { } } - return nil + return tipRecipient, nil } func (p *TxProcessor) NonrefundableGas() uint64 { diff --git a/go-ethereum b/go-ethereum index b0f9c1b40f..555c97f09d 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit b0f9c1b40f48c3c6a121df6a3e7523c02674a418 +Subproject commit 555c97f09df86bc0ca3bfdfc1c96c1f8f91fde4f From 87e2e3ac52ba4a80919dcb3e709f2fa3459ad733 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Tue, 22 Feb 2022 00:18:37 -0600 Subject: [PATCH 089/110] Complete Geth.md todo's --- docs/arbos/Geth.md | 56 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/docs/arbos/Geth.md b/docs/arbos/Geth.md index 590c47f716..5f575369db 100644 --- a/docs/arbos/Geth.md +++ b/docs/arbos/Geth.md @@ -110,14 +110,14 @@ The process is simplified using two functions: [`PrepareRecording`][PrepareRecor Nitro geth includes a few L2-specific transaction types. Click on any to jump to their section. -| Tx Type | Represents | Final hook | Source | -|:--------------------------------------------------|:----------------------------------|:---------------------------|--------| -| [`ArbitrumUnsignedTx`][ArbTxUnsigned] | | [`EndTxHook`][HE] | Bridge | -| [`ArbitrumContractTx`][ArbTxContract] | | [`EndTxHook`][HE] | Bridge | -| [`ArbitrumSubmitRetryableTx`][ArbTxSubmit]   | Creating a retryable | [`StartTxHook`][HS]   | Bridge | -| [`ArbitrumRetryTx`][ArbTxRetry] | A retryable redeem attempt   | [`EndTxHook`][HE] | L2 | -| [`ArbitrumDepositTx`][ArbTxDeposit] | A user deposit | [`StartTxHook`][HS] | Bridge | -| [`ArbitrumInternalTx`][ArbTxInternal] | ArbOS state update | [`StartTxHook`][HS] | ArbOS | +| Tx Type | Represents | Last Hook Reached   | Source | +|:--------------------------------------------------|:-------------------------------------|:---------------------------|--------| +| [`ArbitrumUnsignedTx`][ArbTxUnsigned] | An L1 to L2 message | [`EndTxHook`][HE] | Bridge | +| [`ArbitrumContractTx`][ArbTxContract] | A nonce-less L1 to L2 message   | [`EndTxHook`][HE] | Bridge | +| [`ArbitrumSubmitRetryableTx`][ArbTxSubmit]   | Creating a retryable | [`StartTxHook`][HS]   | Bridge | +| [`ArbitrumRetryTx`][ArbTxRetry] | A retryable redeem attempt | [`EndTxHook`][HE] | L2 | +| [`ArbitrumDepositTx`][ArbTxDeposit] | A user deposit | [`StartTxHook`][HS] | Bridge | +| [`ArbitrumInternalTx`][ArbTxInternal] | ArbOS state update | [`StartTxHook`][HS] | ArbOS | [ArbTxUnsigned]: #ArbitrumUnsignedTx [ArbTxContract]: #ArbitrumContractTx @@ -131,16 +131,16 @@ Nitro geth includes a few L2-specific transaction types. Click on any to jump to The following reference documents each type. ### [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link] -TODO +Provides a mechanism for a user on L1 to message a contract on L2. This uses the bridge for authentication rather than requiring the user's signature. Note, the user's acting address will be remapped on L2 to distinguish them from a normal L2 caller. ### [`ArbitrumContractTx`][ArbitrumContractTx_link] -TODO +These are like an [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link] but are intended for smart contracts. These use the bridge's unique, sequential nonce rather than requiring the caller specify their own. An L1 contract may still use an [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link], but doing so may necessitate tracking the nonce in L1 state. ### [`ArbitrumSubmitRetryableTx`][ArbitrumSubmitRetryableTx_link] -TODO +Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](ArbOS.md#Retryables) for more info. ### [`ArbitrumRetryTx`][ArbitrumRetryTx_link] -These are scheduled by calls to `redeem` and via retryable auto-redemption. The [retryables documentation] details ... TODO +These are scheduled by calls to the [`redeem`](Precompiles.md#ArbRetryableTx) precompile method and via retryable auto-redemption. Please see the [retryables documentation](ArbOS.md#Retryables) for more info. ### [`ArbitrumDepositTx`][ArbitrumDepositTx_link] Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. @@ -163,20 +163,26 @@ Updates the L1 block number. This tx [is generated][block_generated_link] whenev [block_generated_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L150 [block_first_link]: https://github.com/OffchainLabs/nitro/blob/aa55a504d32f71f4ce3a6552822c0791711f8299/arbos/block_processor.go#L154 -## Underlying Transactions and Transaction Run Modes -Though not all geth messages are derived from a transaction, those that are carry their ... TODO +## Transaction Run Modes and Underlying Transactions +A [geth message][geth_message_link] may be processed for various purposes. For example, a message may be used to estimate the gas of a contract call, whereas another may perform the corresponding state transition. Nitro geth denotes the intent behind a message by means of its [`TxRunMode`][TxRunMode_link], [which it sets][set_run_mode_link] before processing it. ArbOS uses this info to make decisions about the tx the message ultimately constructs. + +A message [derived from a transaction][AsMessage_link] will carry that transaction in a field accessible via its [`UnderlyingTransaction`][underlying_link] method. While this is related to the way a given message is used, they are not one-to-one. The table below shows the various run modes and whether each could have an underlying transaction. | Run Mode | Scope | Carries an Underlying Tx? | |:-----------------------------------------|:------------------------|:-----------------------------------------------------------------------------| | [`MessageCommitMode`][MC0] | state transition   | always | -| [`MessageGasEstimationMode`][MC1]   | gas estimation | when created via [`NodeInterface.sol`](#NodeInterface.sol) or when scheduled | +| [`MessageGasEstimationMode`][MC1]   | gas estimation | when created via [`NodeInterface.sol`](Gas.md#NodeInterface.sol) or when scheduled | | [`MessageEthcallMode`][MC2] | eth_calls | never | -[MC0]: todo -[MC1]: todo -[MC2]: todo +[MC0]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L648 +[MC1]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L649 +[MC2]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L650 -TODO +[geth_message_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L628 +[TxRunMode_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L695 +[set_run_mode_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/internal/ethapi/api.go#L911 +[AsMessage_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L670 +[underlying_link]: https://github.com/OffchainLabs/go-ethereum/blob/1e9c9b86135dafebf7ab84641a5674e4249ee849/core/types/transaction.go#L694 ## Arbitrum Chain Parameters Nitro's geth may be configured with the following [l2-specific chain parameters][chain_params_link]. These allow the rollup creator to customize their rollup at genesis. @@ -210,21 +216,21 @@ Retryables are mostly implemented in [ArbOS](ArbOS.md#retryables). Some modifica * Added gasEstimation param to DoCall. When enabled, DoCall will also also executing any retriable activated by the original call. This allows estimating gas to enable retriables. ### Added accessors -Added ['UnderlyingTransaction'][UnderlyingTransaction_link] to Message interface -Added ['GetCurrentTxLogs'](../../go-ethereum/core/state/statedb_arbitrum.go) to StateDB +Added [`UnderlyingTransaction`][UnderlyingTransaction_link] to Message interface +Added [`GetCurrentTxLogs`](../../go-ethereum/core/state/statedb_arbitrum.go) to StateDB We created the AdvancedPrecompile interface, which executes and charges gas with the same function call. This is used by Arbitrum's precompiles, and also wraps geth's standard precompiles. For more information on Arbitrum precompiles, see [ArbOS doc](ArbOS.md#precompiles). ### WASM build support -The WASM arbitrum executable does not support file oprations. We created [fileutil.go](../../go-ethereum/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. ['fake_leveldb.go'](../../go-ethereum/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. +The WASM arbitrum executable does not support file oprations. We created [`fileutil.go`](../../go-ethereum/core/rawdb/fileutil.go) to wrap fileutil calls, stubbing them out when building WASM. [`fake_leveldb.go`](../../go-ethereum/ethdb/leveldb/fake_leveldb.go) is a similar WASM-mock for leveldb. These are not required for the WASM block-replayer. ### Types -Arbitrum introduces a new ['signer'](../../go-ethereum/core/types/arbitrum_signer.go), and multiple new [`transaction types`](../../go-ethereum/core/types/transaction.go). +Arbitrum introduces a new [`signer`](../../go-ethereum/core/types/arbitrum_signer.go), and multiple new [`transaction types`](../../go-ethereum/core/types/transaction.go). ### ReorgToOldBlock -Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the ['ReorgToOldBlock'](../../go-ethereum/core/blockchain_arbitrum.go) function to support reorging to a block that's an ancestor of current head. +Geth natively only allows reorgs to a fork of the currently-known network. In nitro, reorgs can sometimes be detected before computing the forked block. We added the [`ReorgToOldBlock`](../../go-ethereum/core/blockchain_arbitrum.go) function to support reorging to a block that's an ancestor of current head. ### Genesis block creation -Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out ['WriteHeadBlock'][WriteHeadBlock_link] from gensis.Commit and use it to commit non-zero genesis blocks. +Genesis block in nitro is not necessarily block #0. Nitro supports importing blocks that take place before genesis. We split out [`WriteHeadBlock`][WriteHeadBlock_link] from gensis.Commit and use it to commit non-zero genesis blocks. [pad_estimates_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/accounts/abi/bind/base.go#L352 [conservation_link]: https://github.com/OffchainLabs/go-ethereum/blob/0ba62aab54fd7d6f1570a235f4e3a877db9b2bd0/core/state/statedb.go#L42 From 9d6ff60210bb84adb0d042a78e3bcea5282e081a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Mon, 21 Feb 2022 22:56:09 -0600 Subject: [PATCH 090/110] Address comments on staker PR --- arbnode/transaction_streamer.go | 16 ++- arbutil/wait_for_l1.go | 3 + solgen/src/challenge/ChallengeManager.sol | 1 + solgen/src/mocks/SequencerInboxStub.sol | 1 + solgen/src/rollup/IRollupCore.sol | 8 ++ solgen/src/rollup/RollupCore.sol | 34 +++-- solgen/src/rollup/RollupUserLogic.sol | 7 +- solgen/src/rollup/ValidatorUtils.sol | 60 +++----- system_tests/staker_test.go | 59 +++++--- validator/assertion.go | 9 ++ validator/builder_backend.go | 21 ++- validator/challenge_manager.go | 7 +- validator/l1_validator.go | 108 +++++++++------ validator/rollup_watcher.go | 17 ++- validator/staker.go | 88 ++++++------ validator/validator_utils.go | 162 ---------------------- validator/validator_wallet.go | 2 +- 17 files changed, 256 insertions(+), 347 deletions(-) delete mode 100644 validator/validator_utils.go diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 72c8acc0a7..b147181007 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -372,10 +372,6 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa DelayedMessagesRead: delayedMessagesRead, } - if s.validator != nil { - s.validator.NewBlock(block, lastBlockHeader, msgWithMeta) - } - if err := s.writeMessages(pos, []arbstate.MessageWithMetadata{msgWithMeta}, nil); err != nil { return err } @@ -384,6 +380,8 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa s.broadcastServer.BroadcastSingle(msgWithMeta, pos) } + // Only write the block after we've written the messages, so if the node dies in the middle of this, + // it will naturally recover on startup by regenerating the missing block. var logs []*types.Log for _, receipt := range receipts { logs = append(logs, receipt.Logs...) @@ -396,6 +394,10 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa return errors.New("geth rejected block as non-canonical") } + if s.validator != nil { + s.validator.NewBlock(block, lastBlockHeader, msgWithMeta) + } + return nil } @@ -434,6 +436,12 @@ func (s *TransactionStreamer) SequenceDelayedMessages(ctx context.Context, messa return err } + for i, msg := range messagesWithMeta { + if s.broadcastServer != nil { + s.broadcastServer.BroadcastSingle(msg, pos+arbutil.MessageIndex(i)) + } + } + expectedBlockNum, err := s.MessageCountToBlockNumber(pos) if err != nil { return err diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index 4a23f1cbe0..8704aa0252 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -93,6 +93,9 @@ func WaitForTx(ctxinput context.Context, client L1Interface, txhash common.Hash, return receipt, nil } } + if !timer.Stop() { + <-timer.C + } timer.Reset(checkInterval) select { case <-chanHead: diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 1eb75b85da..28a5b3ddbb 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -101,6 +101,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.wasmModuleRoot = wasmModuleRoot_; + // See validator/assertion.go ExecutionState RequiredBatches() for reasoning uint64 maxInboxMessagesRead = startAndEndGlobalStates_[1].getInboxPosition(); if (startAndEndMachineStatuses_[1] == MachineStatus.ERRORED || startAndEndGlobalStates_[1].getPositionInMessage() > 0) { maxInboxMessagesRead++; diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index 3f63ffdf5c..417f8ef9bf 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -37,6 +37,7 @@ contract SequencerInboxStub is SequencerInbox { } function getTimeBounds() internal view override returns (TimeBounds memory bounds) { + this; // silence warning about function not being view return bounds; } } diff --git a/solgen/src/rollup/IRollupCore.sol b/solgen/src/rollup/IRollupCore.sol index b7077da2bb..f066e53348 100644 --- a/solgen/src/rollup/IRollupCore.sol +++ b/solgen/src/rollup/IRollupCore.sol @@ -39,6 +39,7 @@ interface IRollupCore { uint64 indexed nodeNum, bytes32 indexed parentNodeHash, bytes32 indexed nodeHash, + bytes32 executionHash, RollupLib.Assertion assertion, bytes32 afterInboxBatchAcc, bytes32 wasmModuleRoot, @@ -115,6 +116,13 @@ interface IRollupCore { */ function amountStaked(address staker) external view returns (uint256); + /** + * @notice Retrieves stored information about a requested staker + * @param staker Staker address to retrieve + * @return A structure with information about the requested staker + */ + function getStaker(address staker) external view returns (Staker memory); + /** * @notice Get the original staker address of the zombie at the given index * @param zombieNum Index of the zombie to lookup diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 88a5d5b1c0..97eede823f 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -63,8 +63,6 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { uint64 latestStakedNode; } - - uint64 private _latestConfirmed; uint64 private _firstUnresolvedNode; uint64 private _latestNodeCreated; @@ -182,6 +180,15 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { return _stakerMap[staker].amountStaked; } + /** + * @notice Retrieves stored information about a requested staker + * @param staker Staker address to retrieve + * @return A structure with information about the requested staker + */ + function getStaker(address staker) external view override returns (Staker memory) { + return _stakerMap[staker]; + } + /** * @notice Get the original staker address of the zombie at the given index * @param zombieNum Index of the zombie to lookup @@ -580,20 +587,27 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { // Make sure the previous state is correct against the node being built on require( - RollupLib.stateHash(assertion.beforeState, prevNodeInboxMaxCount) == - memoryFrame.prevNode.stateHash, + RollupLib.stateHash( + assertion.beforeState, + prevNodeInboxMaxCount + ) == memoryFrame.prevNode.stateHash, "PREV_STATE_HASH" ); // Ensure that the assertion doesn't read past the end of the current inbox - uint256 afterInboxCount = assertion.afterState.globalState.getInboxPosition(); + uint256 afterInboxCount = assertion + .afterState + .globalState + .getInboxPosition(); require( - afterInboxCount >= assertion.beforeState.globalState.getInboxPosition(), + afterInboxCount >= + assertion.beforeState.globalState.getInboxPosition(), "INBOX_BACKWARDS" ); + // See validator/assertion.go ExecutionState RequiredBatches() for reasoning if ( assertion.afterState.machineStatus == MachineStatus.ERRORED || - assertion.afterState.globalState.getPositionInMessage() > 0 + assertion.afterState.globalState.getPositionInMessage() > 0 ) { // The current inbox message was read afterInboxCount++; @@ -636,7 +650,10 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { require(newNodeHash == expectedNodeHash, "UNEXPECTED_NODE_HASH"); memoryFrame.node = NodeLib.createNode( - RollupLib.stateHash(assertion.afterState, memoryFrame.currentInboxSize), + RollupLib.stateHash( + assertion.afterState, + memoryFrame.currentInboxSize + ), RollupLib.challengeRootHash( memoryFrame.executionHash, block.number, @@ -670,6 +687,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { latestNodeCreated(), memoryFrame.prevNode.nodeHash, newNodeHash, + memoryFrame.executionHash, assertion, memoryFrame.sequencerBatchAcc, wasmModuleRoot, diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 400a044fff..c6b164c61e 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -186,9 +186,12 @@ abstract contract AbsRollupUserLogic is require(timeSinceLastNode >= minimumAssertionPeriod, "TIME_DELTA"); // Minimum size requirement: any assertion must consume at least all inbox messages - // put into L1 inbox before the prev node’s L1 blocknum + // put into L1 inbox before the prev node’s L1 blocknum. + // We make an exception if the machine enters the errored state, + // as it can't consume future batches. require( - assertion.afterState.globalState.getInboxPosition() >= + assertion.afterState.machineStatus == MachineStatus.ERRORED || + assertion.afterState.globalState.getInboxPosition() >= prevNodeInboxMaxCount, "TOO_SMALL" ); diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index bf27f94bee..04abde5670 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -35,29 +35,17 @@ contract ValidatorUtils { INVALID } - enum NodeConflict { + enum NodeConflictType { NONE, FOUND, INDETERMINATE, INCOMPLETE } - function stakerInfo(IRollupCore rollup, address stakerAddress) - external - view - returns ( - bool isStaked, - uint64 latestStakedNode, - uint256 amountStaked, - uint64 currentChallenge - ) - { - return ( - rollup.isStaked(stakerAddress), - rollup.latestStakedNode(stakerAddress), - rollup.amountStaked(stakerAddress), - rollup.currentChallenge(stakerAddress) - ); + struct NodeConflict { + NodeConflictType ty; + uint64 node1; + uint64 node2; } function findStakerConflict( @@ -68,11 +56,7 @@ contract ValidatorUtils { ) external view - returns ( - NodeConflict, - uint64, - uint64 - ) + returns (NodeConflict memory) { uint64 staker1NodeNum = rollup.latestStakedNode(staker1); uint64 staker2NodeNum = rollup.latestStakedNode(staker2); @@ -91,12 +75,11 @@ contract ValidatorUtils { } } - function requireRejectable(IRollupCore rollup) external view returns (bool) { + function requireRejectable(IRollupCore rollup) external view { IRollupUser(address(rollup)).requireUnresolvedExists(); uint64 firstUnresolvedNode = rollup.firstUnresolvedNode(); Node memory node = rollup.getNode(firstUnresolvedNode); - bool inOrder = node.prevNum == rollup.latestConfirmed(); - if (inOrder) { + if (node.prevNum == rollup.latestConfirmed()) { // Verify the block's deadline has passed require(block.number >= node.deadlineBlock, "BEFORE_DEADLINE"); rollup.getNode(node.prevNum).requirePastChildConfirmDeadline(); @@ -107,7 +90,6 @@ contract ValidatorUtils { "HAS_STAKERS" ); } - return inOrder; } function requireConfirmable(IRollupCore rollup) external view { @@ -154,13 +136,13 @@ contract ValidatorUtils { return stakers; } - function latestStaked(IRollupCore rollup, address staker) external view returns (uint64, bytes32) { - uint64 node = rollup.latestStakedNode(staker); - if (node == 0) { - node = rollup.latestConfirmed(); + function latestStaked(IRollupCore rollup, address staker) external view returns (uint64, Node memory) { + uint64 num = rollup.latestStakedNode(staker); + if (num == 0) { + num = rollup.latestConfirmed(); } - bytes32 acc = rollup.getNode(node).nodeHash; - return (node, acc); + Node memory node = rollup.getNode(num); + return (num, node); } function stakedNodes(IRollupCore rollup, address staker) external view returns (uint64[] memory) { @@ -187,11 +169,7 @@ contract ValidatorUtils { ) public view - returns ( - NodeConflict, - uint64, - uint64 - ) + returns (NodeConflict memory) { uint64 firstUnresolvedNode = rollup.firstUnresolvedNode(); uint64 node1Prev = rollup.getNode(node1).prevNum; @@ -199,13 +177,13 @@ contract ValidatorUtils { for (uint256 i = 0; i < maxDepth; i++) { if (node1 == node2) { - return (NodeConflict.NONE, node1, node2); + return NodeConflict(NodeConflictType.NONE, node1, node2); } if (node1Prev == node2Prev) { - return (NodeConflict.FOUND, node1, node2); + return NodeConflict(NodeConflictType.FOUND, node1, node2); } if (node1Prev < firstUnresolvedNode && node2Prev < firstUnresolvedNode) { - return (NodeConflict.INDETERMINATE, 0, 0); + return NodeConflict(NodeConflictType.INDETERMINATE, 0, 0); } if (node1Prev < node2Prev) { node2 = node2Prev; @@ -215,7 +193,7 @@ contract ValidatorUtils { node1Prev = rollup.getNode(node1).prevNum; } } - return (NodeConflict.INCOMPLETE, node1, node2); + return NodeConflict(NodeConflictType.INCOMPLETE, 0, 0); } function getStakers( diff --git a/system_tests/staker_test.go b/system_tests/staker_test.go index 69af5d99ff..7d94635f70 100644 --- a/system_tests/staker_test.go +++ b/system_tests/staker_test.go @@ -10,9 +10,9 @@ package arbtest import ( "context" + "errors" "fmt" "math/big" - "sync/atomic" "testing" "time" @@ -24,8 +24,38 @@ import ( "github.com/offchainlabs/arbstate/validator" ) +func makeBackgroundTxs(ctx context.Context, l2info *BlockchainTestInfo, l2clientA arbutil.L1Interface, l2clientB arbutil.L1Interface, faultyStaker bool) error { + for i := uint64(0); ctx.Err() == nil; i++ { + l2info.Accounts["BackgroundUser"].Nonce = i + tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) + err := l2clientA.SendTransaction(ctx, tx) + if err != nil { + return err + } + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) + if err != nil { + return err + } + if faultyStaker { + // Create a different transaction for the second node + l2info.Accounts["BackgroundUser"].Nonce = i + tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) + err = l2clientB.SendTransaction(ctx, tx) + if err != nil { + return err + } + _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) + if err != nil { + return err + } + } + } + return nil +} + func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) { - ctx := context.Background() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() l2info, l2nodeA, l2clientA, l1info, _, l1client, l1stack := CreateTestNodeOnL1(t, ctx, true) defer l1stack.Close() @@ -150,30 +180,17 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) } // Continually make L2 transactions in a background thread - var stopBackgroundTxs int32 + backgroundTxsCtx, cancelBackgroundTxs := context.WithCancel(ctx) backgroundTxsShutdownChan := make(chan struct{}) defer (func() { - atomic.StoreInt32(&stopBackgroundTxs, 1) + cancelBackgroundTxs() <-backgroundTxsShutdownChan })() go (func() { defer close(backgroundTxsShutdownChan) - for i := uint64(0); atomic.LoadInt32(&stopBackgroundTxs) == 0; i++ { - l2info.Accounts["BackgroundUser"].Nonce = i - tx := l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big0, nil) - err := l2clientA.SendTransaction(ctx, tx) - Require(t, err) - _, err = arbutil.EnsureTxSucceeded(ctx, l2clientA, tx) - Require(t, err) - if faultyStaker { - // Create a different transaction for the second node - l2info.Accounts["BackgroundUser"].Nonce = i - tx = l2info.PrepareTx("BackgroundUser", "BackgroundUser", l2info.TransferGas, common.Big1, nil) - err = l2clientB.SendTransaction(ctx, tx) - Require(t, err) - _, err = arbutil.EnsureTxSucceeded(ctx, l2clientB, tx) - Require(t, err) - } + err := makeBackgroundTxs(backgroundTxsCtx, l2info, l2clientA, l2clientB, faultyStaker) + if !errors.Is(err, context.Canceled) { + t.Error("error making background txs", err) } })() @@ -206,7 +223,7 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool) challengeAddr, err := rollup.CurrentChallenge(&bind.CallOpts{}, valWalletAddrA) Require(t, err) if challengeAddr != 0 { - atomic.StoreInt32(&stopBackgroundTxs, 1) + cancelBackgroundTxs() } } if faultyStaker && !sawStakerZombie { diff --git a/validator/assertion.go b/validator/assertion.go index 31225e1e81..f57c379cae 100644 --- a/validator/assertion.go +++ b/validator/assertion.go @@ -63,6 +63,15 @@ func (s *ExecutionState) BlockStateHash() common.Hash { } } +// Determine the batch count required to reach the execution state. +// If the machine errored or the state is after the beginning of the batch, +// the current batch is required to reach the state. +// That's because if the machine errored, it might've read the current batch before erroring, +// and if it's in the middle of a batch, it had to read prior parts of the batch to get there. +// However, if the machine finished successfully and the new state is the start of the batch, +// it hasn't read the batch yet, as it just finished the last batch. +// +// This logic is replicated in Solidity in a few places; search for RequiredBatches to find them. func (s *ExecutionState) RequiredBatches() uint64 { count := s.GlobalState.Batch if (s.MachineStatus == MachineStatusErrored || s.GlobalState.PosInBatch > 0) && count < math.MaxUint64 { diff --git a/validator/builder_backend.go b/validator/builder_backend.go index 433b4f1bb8..dd0839fd37 100644 --- a/validator/builder_backend.go +++ b/validator/builder_backend.go @@ -17,16 +17,15 @@ import ( "github.com/pkg/errors" ) -type BuilderBackend struct { +type ValidatorTxBuilder struct { + arbutil.L1Interface transactions []*types.Transaction builderAuth *bind.TransactOpts realSender common.Address wallet *ValidatorWallet - - arbutil.L1Interface } -func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { +func NewValidatorTxBuilder(wallet *ValidatorWallet) (*ValidatorTxBuilder, error) { randKey, err := crypto.GenerateKey() if err != nil { return nil, err @@ -35,7 +34,7 @@ func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { if err != nil { return nil, err } - return &BuilderBackend{ + return &ValidatorTxBuilder{ builderAuth: fakeAuth, realSender: wallet.From(), wallet: wallet, @@ -43,19 +42,19 @@ func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) { }, nil } -func (b *BuilderBackend) BuilderTransactionCount() int { +func (b *ValidatorTxBuilder) BuildingTransactionCount() int { return len(b.transactions) } -func (b *BuilderBackend) ClearTransactions() { +func (b *ValidatorTxBuilder) ClearTransactions() { b.transactions = nil } -func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { +func (b *ValidatorTxBuilder) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error) { return 0, nil } -func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { +func (b *ValidatorTxBuilder) SendTransaction(ctx context.Context, tx *types.Transaction) error { b.transactions = append(b.transactions, tx) data, dest, amount, totalAmount := combineTxes(b.transactions) if b.wallet.Address() == nil { @@ -75,7 +74,7 @@ func (b *BuilderBackend) SendTransaction(ctx context.Context, tx *types.Transact return errors.WithStack(err) } -func (b *BuilderBackend) AuthWithAmount(ctx context.Context, amount *big.Int) *bind.TransactOpts { +func (b *ValidatorTxBuilder) AuthWithAmount(ctx context.Context, amount *big.Int) *bind.TransactOpts { return &bind.TransactOpts{ From: b.builderAuth.From, Nonce: b.builderAuth.Nonce, @@ -87,6 +86,6 @@ func (b *BuilderBackend) AuthWithAmount(ctx context.Context, amount *big.Int) *b } } -func (b *BuilderBackend) Auth(ctx context.Context) *bind.TransactOpts { +func (b *ValidatorTxBuilder) Auth(ctx context.Context) *bind.TransactOpts { return b.AuthWithAmount(ctx, big.NewInt(0)) } diff --git a/validator/challenge_manager.go b/validator/challenge_manager.go index b13be49d76..edbfee62aa 100644 --- a/validator/challenge_manager.go +++ b/validator/challenge_manager.go @@ -8,6 +8,8 @@ import ( "context" "encoding/binary" "fmt" + "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" @@ -18,7 +20,6 @@ import ( "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/challengegen" "github.com/pkg/errors" - "math/big" ) const maxBisectionDegree uint64 = 40 @@ -444,7 +445,7 @@ func (m *ChallengeManager) createInitialMachine(ctx context.Context, blockNum in return nil } -func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { +func (m *ChallengeManager) LoadExecChallengeIfExists(ctx context.Context) error { if m.executionChallengeBackend != nil { return nil } @@ -493,7 +494,7 @@ func (m *ChallengeManager) TestExecChallenge(ctx context.Context) error { } func (m *ChallengeManager) Act(ctx context.Context) (*types.Transaction, error) { - err := m.TestExecChallenge(ctx) + err := m.LoadExecChallengeIfExists(ctx) if err != nil { return nil, err } diff --git a/validator/l1_validator.go b/validator/l1_validator.go index 87c155ef5b..aa9cd4d954 100644 --- a/validator/l1_validator.go +++ b/validator/l1_validator.go @@ -17,28 +17,39 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/arbstate/arbos" "github.com/offchainlabs/arbstate/arbutil" - "github.com/offchainlabs/arbstate/solgen/go/bridgegen" "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/pkg/errors" ) +type ConfirmType uint8 + +const ( + CONFIRM_TYPE_NONE ConfirmType = iota + CONFIRM_TYPE_VALID + CONFIRM_TYPE_INVALID +) + +type ConflictType uint8 + +const ( + CONFLICT_TYPE_NONE ConflictType = iota + CONFLICT_TYPE_FOUND + CONFLICT_TYPE_INDETERMINATE + CONFLICT_TYPE_INCOMPLETE +) + type Validator struct { rollup *RollupWatcher rollupAddress common.Address challengeManagerAddress common.Address - sequencerInbox *bridgegen.SequencerInbox validatorUtils *rollupgen.ValidatorUtils client arbutil.L1Interface - builder *BuilderBackend + builder *ValidatorTxBuilder wallet *ValidatorWallet callOpts bind.CallOpts - GasThreshold *big.Int - SendThreshold *big.Int - BlockThreshold *big.Int genesisBlockNumber uint64 l2Blockchain *core.BlockChain - inboxReader InboxReaderInterface inboxTracker InboxTrackerInterface txStreamer TransactionStreamerInterface blockValidator *BlockValidator @@ -51,12 +62,11 @@ func NewValidator( validatorUtilsAddress common.Address, callOpts bind.CallOpts, l2Blockchain *core.BlockChain, - inboxReader InboxReaderInterface, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, blockValidator *BlockValidator, ) (*Validator, error) { - builder, err := NewBuilderBackend(wallet) + builder, err := NewValidatorTxBuilder(wallet) if err != nil { return nil, err } @@ -66,18 +76,10 @@ func NewValidator( } localCallOpts := callOpts localCallOpts.Context = ctx - sequencerBridgeAddress, err := rollup.SequencerBridge(&localCallOpts) - if err != nil { - return nil, err - } challengeManagerAddress, err := rollup.ChallengeManager(&localCallOpts) if err != nil { return nil, err } - sequencerInbox, err := bridgegen.NewSequencerInbox(sequencerBridgeAddress, client) - if err != nil { - return nil, err - } validatorUtils, err := rollupgen.NewValidatorUtils( validatorUtilsAddress, client, @@ -93,18 +95,13 @@ func NewValidator( rollup: rollup, rollupAddress: wallet.RollupAddress(), challengeManagerAddress: challengeManagerAddress, - sequencerInbox: sequencerInbox, validatorUtils: validatorUtils, client: client, builder: builder, wallet: wallet, - GasThreshold: big.NewInt(100_000_000_000), - SendThreshold: big.NewInt(5), - BlockThreshold: big.NewInt(960), callOpts: callOpts, genesisBlockNumber: genesisBlockNumber, l2Blockchain: l2Blockchain, - inboxReader: inboxReader, inboxTracker: inboxTracker, txStreamer: txStreamer, blockValidator: blockValidator, @@ -117,8 +114,8 @@ func (v *Validator) getCallOpts(ctx context.Context) *bind.CallOpts { return &opts } -// removeOldStakers removes the stakes of all currently staked validators except -// its own if dontRemoveSelf is true +// removeOldStakers removes the stakes of all validators staked on the latest confirmed node (aka "refundable" or "old" stakers), +// except its own if dontRemoveSelf is true func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) (*types.Transaction, error) { stakersToEliminate, err := v.validatorUtils.RefundableStakers(v.getCallOpts(ctx), v.rollupAddress) if err != nil { @@ -319,8 +316,7 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake return nil, false, nil } - // Not necessarily successors - successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNode) + successorNodes, err := v.rollup.LookupNodeChildren(ctx, stakerInfo.LatestStakedNode, stakerInfo.LatestStakedNodeHash) if err != nil { return nil, false, err } @@ -415,27 +411,46 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake wrongNodesExist = true } - if strategy == WatchtowerStrategy || correctNode != nil || (strategy < MakeNodesStrategy && !wrongNodesExist) { - return correctNode, wrongNodesExist, nil + if strategy > WatchtowerStrategy && correctNode == nil && (strategy >= MakeNodesStrategy || wrongNodesExist) { + // There's no correct node; create one. + var lastNodeHashIfExists *common.Hash + if len(successorNodes) > 0 { + lastNodeHashIfExists = &successorNodes[len(successorNodes)-1].NodeHash + } + action, err := v.createNewNodeAction(ctx, stakerInfo, blocksValidated, localBatchCount, prevInboxMaxCount, startBlock, startState, lastNodeHashIfExists) + return action, wrongNodesExist, err } + return correctNode, wrongNodesExist, nil +} + +func (v *Validator) createNewNodeAction( + ctx context.Context, + stakerInfo *OurStakerInfo, + blocksValidated uint64, + localBatchCount uint64, + prevInboxMaxCount *big.Int, + startBlock *types.Block, + startState *ExecutionState, + lastNodeHashIfExists *common.Hash, +) (nodeAction, error) { if !prevInboxMaxCount.IsUint64() { - return nil, false, fmt.Errorf("inbox max count %v isn't a uint64", prevInboxMaxCount) + return nil, fmt.Errorf("inbox max count %v isn't a uint64", prevInboxMaxCount) } minBatchCount := prevInboxMaxCount.Uint64() if localBatchCount < minBatchCount { // not enough batches in database - return nil, wrongNodesExist, nil + return nil, nil } if blocksValidated == 0 || localBatchCount == 0 { // we haven't validated anything - return nil, wrongNodesExist, nil + return nil, nil } lastBlockValidated := blocksValidated - 1 if startBlock != nil && lastBlockValidated <= startBlock.NumberU64() { // we haven't validated any new blocks - return nil, wrongNodesExist, nil + return nil, nil } var assertionCoversBatch uint64 var afterGsBatch uint64 @@ -443,16 +458,19 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake for i := localBatchCount - 1; i+1 >= minBatchCount && i > 0; i-- { batchMessageCount, err := v.inboxTracker.GetBatchMessageCount(i) if err != nil { - return nil, false, err + return nil, err } prevBatchMessageCount, err := v.inboxTracker.GetBatchMessageCount(i - 1) if err != nil { - return nil, false, err + return nil, err } // Must be non-negative as a batch must contain at least one message lastBlockNum := uint64(arbutil.MessageCountToBlockNumber(batchMessageCount, v.genesisBlockNumber)) prevBlockNum := uint64(arbutil.MessageCountToBlockNumber(prevBatchMessageCount, v.genesisBlockNumber)) - if lastBlockValidated > prevBlockNum && lastBlockValidated <= lastBlockNum { + if lastBlockValidated > lastBlockNum { + return nil, fmt.Errorf("%v blocks have been validated but only %v appear in the latest batch", lastBlockValidated, lastBlockNum) + } + if lastBlockValidated > prevBlockNum { // We found the batch containing the last validated block if i+1 == minBatchCount && lastBlockValidated < lastBlockNum { // We haven't reached the minimum assertion size yet @@ -471,29 +489,27 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake } if assertionCoversBatch == 0 { // we haven't validated the next batch completely - return nil, wrongNodesExist, nil + return nil, nil } validatedBatchAcc, err := v.inboxTracker.GetBatchAcc(assertionCoversBatch) if err != nil { - return nil, false, err + return nil, err } assertingBlock := v.l2Blockchain.GetBlockByNumber(lastBlockValidated) if assertingBlock == nil { - return nil, false, fmt.Errorf("missing validated block %v", lastBlockValidated) + return nil, fmt.Errorf("missing validated block %v", lastBlockValidated) } assertingBlockExtra, err := arbos.DeserializeHeaderExtraInformation(assertingBlock.Header()) if err != nil { - return nil, false, err + return nil, err } hasSiblingByte := [1]byte{0} - lastNum := stakerInfo.LatestStakedNode + prevNum := stakerInfo.LatestStakedNode lastHash := stakerInfo.LatestStakedNodeHash - if len(successorNodes) > 0 { - lastSuccessor := successorNodes[len(successorNodes)-1] - lastNum = lastSuccessor.NodeNum - lastHash = lastSuccessor.NodeHash + if lastNodeHashIfExists != nil { + lastHash = *lastNodeHashIfExists hasSiblingByte[0] = 1 } var assertionNumBlocks uint64 @@ -524,8 +540,8 @@ func (v *Validator) generateNodeAction(ctx context.Context, stakerInfo *OurStake hash: newNodeHash, prevInboxMaxCount: prevInboxMaxCount, } - log.Info("creating node", "hash", newNodeHash, "lastNode", lastNum, "parentNode", stakerInfo.LatestStakedNode) - return action, wrongNodesExist, nil + log.Info("creating node", "hash", newNodeHash, "lastNode", prevNum, "parentNode", stakerInfo.LatestStakedNode) + return action, nil } // Returns (execution state, inbox max count, block proposed, error) diff --git a/validator/rollup_watcher.go b/validator/rollup_watcher.go index 6a10e00e7b..8ee23c5662 100644 --- a/validator/rollup_watcher.go +++ b/validator/rollup_watcher.go @@ -7,9 +7,11 @@ package validator import ( "context" "encoding/binary" + "fmt" "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/rollupgen" "github.com/pkg/errors" @@ -40,11 +42,11 @@ type StakerInfo struct { } type RollupWatcher struct { + *rollupgen.RollupUserLogic address common.Address fromBlock uint64 client arbutil.L1Interface baseCallOpts bind.CallOpts - *rollupgen.RollupUserLogic } func NewRollupWatcher(ctx context.Context, address common.Address, client arbutil.L1Interface, callOpts bind.CallOpts) (*RollupWatcher, error) { @@ -139,7 +141,7 @@ func (r *RollupWatcher) LookupNode(ctx context.Context, number uint64) (*NodeInf }, nil } -func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64) ([]*NodeInfo, error) { +func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64, nodeHash common.Hash) ([]*NodeInfo, error) { node, err := r.RollupUserLogic.GetNode(r.getCallOpts(ctx), nodeNum) if err != nil { return nil, errors.WithStack(err) @@ -147,6 +149,9 @@ func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64) if node.LatestChildNumber == 0 { return nil, nil } + if node.NodeHash != nodeHash { + return nil, fmt.Errorf("Got unexpected node hash %v looking for node number %v with expected hash %v (reorg?)", node.NodeHash, nodeNum, nodeHash) + } latestChild, err := r.RollupUserLogic.GetNode(r.getCallOpts(ctx), node.LatestChildNumber) if err != nil { return nil, errors.WithStack(err) @@ -155,13 +160,14 @@ func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64) FromBlock: new(big.Int).SetUint64(node.CreatedAtBlock), ToBlock: new(big.Int).SetUint64(latestChild.CreatedAtBlock), Addresses: []common.Address{r.address}, - Topics: [][]common.Hash{{nodeCreatedID}, nil, {node.NodeHash}}, + Topics: [][]common.Hash{{nodeCreatedID}, nil, {nodeHash}}, } logs, err := r.client.FilterLogs(ctx, query) if err != nil { return nil, errors.WithStack(err) } infos := make([]*NodeInfo, 0, len(logs)) + lastHash := nodeHash for i, ethLog := range logs { parsedLog, err := r.ParseNodeCreated(ethLog) if err != nil { @@ -171,18 +177,17 @@ func (r *RollupWatcher) LookupNodeChildren(ctx context.Context, nodeNum uint64) if i > 0 { lastHashIsSibling[0] = 1 } + lastHash = crypto.Keccak256Hash(lastHashIsSibling[:], lastHash[:], parsedLog.ExecutionHash[:], parsedLog.AfterInboxBatchAcc[:]) infos = append(infos, &NodeInfo{ NodeNum: parsedLog.NodeNum, BlockProposed: ethLog.BlockNumber, Assertion: NewAssertionFromSolidity(parsedLog.Assertion), InboxMaxCount: parsedLog.InboxMaxCount, AfterInboxBatchAcc: parsedLog.AfterInboxBatchAcc, - NodeHash: parsedLog.NodeHash, + NodeHash: lastHash, WasmModuleRoot: parsedLog.WasmModuleRoot, }) } - // TODO: If we want to verify consistency here, we can that that the node hash of the last node - // found matches latestChild since it encompasses all preceding nodes return infos, nil } diff --git a/validator/staker.go b/validator/staker.go index c1a592cc99..e5b063f020 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -26,9 +26,13 @@ const txTimeout time.Duration = 5 * time.Minute type StakerStrategy uint8 const ( + // Watchtower: don't do anything on L1, but log if there's a bad assertion WatchtowerStrategy StakerStrategy = iota + // Defensive: stake if there's a bad assertion DefensiveStrategy + // Stake latest: stay staked on the latest node, challenging bad assertions StakeLatestStrategy + // Make nodes: continually create new nodes, challenging bad assertions MakeNodesStrategy ) @@ -66,11 +70,21 @@ type Staker struct { inactiveLastCheckedNode *nodeAndHash bringActiveUntilNode uint64 withdrawDestination common.Address + inboxReader InboxReaderInterface +} - l2Blockchain *core.BlockChain - inboxReader InboxReaderInterface - inboxTracker InboxTrackerInterface - txStreamer TransactionStreamerInterface +func stakerStrategyFromString(s string) (StakerStrategy, error) { + if strings.ToLower(s) == "watchtower" { + return WatchtowerStrategy, nil + } else if strings.ToLower(s) == "defensive" { + return DefensiveStrategy, nil + } else if strings.ToLower(s) == "stakelatest" { + return StakeLatestStrategy, nil + } else if strings.ToLower(s) == "makenodes" { + return MakeNodesStrategy, nil + } else { + return WatchtowerStrategy, fmt.Errorf("unknown staker strategy \"%v\"", s) + } } func NewStaker( @@ -90,19 +104,11 @@ func NewStaker( return nil, fmt.Errorf("invalid validator utils address \"%v\"", config.UtilsAddress) } validatorUtilsAddress := common.HexToAddress(config.UtilsAddress) - var strategy StakerStrategy - if strings.ToLower(config.Strategy) == "watchtower" { - strategy = WatchtowerStrategy - } else if strings.ToLower(config.Strategy) == "defensive" { - strategy = DefensiveStrategy - } else if strings.ToLower(config.Strategy) == "stakelatest" { - strategy = StakeLatestStrategy - } else if strings.ToLower(config.Strategy) == "makenodes" { - strategy = MakeNodesStrategy - } else { - return nil, fmt.Errorf("unknown staker strategy \"%v\"", config.Strategy) + strategy, err := stakerStrategyFromString(config.Strategy) + if err != nil { + return nil, err } - val, err := NewValidator(ctx, client, wallet, validatorUtilsAddress, callOpts, l2Blockchain, inboxReader, inboxTracker, txStreamer, blockValidator) + val, err := NewValidator(ctx, client, wallet, validatorUtilsAddress, callOpts, l2Blockchain, inboxTracker, txStreamer, blockValidator) if err != nil { return nil, err } @@ -119,15 +125,11 @@ func NewStaker( highGasBlocksBuffer: big.NewInt(config.L1PostingStrategy.HighGasDelayBlocks), lastActCalledBlock: nil, withdrawDestination: withdrawDestination, - - l2Blockchain: l2Blockchain, - inboxReader: inboxReader, - inboxTracker: inboxTracker, - txStreamer: txStreamer, + inboxReader: inboxReader, }, nil } -func (s *Staker) RunInBackground(ctx context.Context, stakerDelay time.Duration) chan bool { +func (s *Staker) Start(ctx context.Context) chan bool { done := make(chan bool) go func() { defer func() { @@ -137,7 +139,6 @@ func (s *Staker) RunInBackground(ctx context.Context, stakerDelay time.Duration) for { arbTx, err := s.Act(ctx) if err == nil && arbTx != nil { - // Note: methodName isn't accurate, it's just used for logging _, err = arbutil.EnsureTxSucceededWithTimeout(ctx, s.client, arbTx, txTimeout) err = errors.Wrap(err, "error waiting for tx receipt") if err == nil { @@ -158,7 +159,11 @@ func (s *Staker) RunInBackground(ctx context.Context, stakerDelay time.Duration) } else { backoff = time.Second } - time.Sleep(stakerDelay) + select { + case <-time.After(s.config.StakerDelay): + default: + return + } } }() return done @@ -191,7 +196,7 @@ func (s *Staker) shouldAct(ctx context.Context) bool { // We're eating into the high gas buffer to delay our tx s.highGasBlocksBuffer.Sub(s.highGasBlocksBuffer, blocksSinceActCalled) } else { - // We'll make a tx if necessary, so we can add to the buffer for future high gas + // We'll try to make a tx if necessary, so we can add to the buffer for future high gas s.highGasBlocksBuffer.Add(s.highGasBlocksBuffer, blocksSinceActCalled) } // Clamp `s.highGasBlocksBuffer` to between 0 and HighGasDelayBlocks @@ -235,17 +240,17 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } // If the wallet address is zero, or the wallet address isn't staked, // this will return the latest node and its hash (atomically). - latestStakedNode, latestStakedNodeHash, err := s.validatorUtils.LatestStaked(callOpts, s.rollupAddress, walletAddressOrZero) + latestStakedNodeNum, latestStakedNodeInfo, err := s.validatorUtils.LatestStaked(callOpts, s.rollupAddress, walletAddressOrZero) if err != nil { return nil, err } if rawInfo != nil { - rawInfo.LatestStakedNode = latestStakedNode + rawInfo.LatestStakedNode = latestStakedNodeNum } info := OurStakerInfo{ CanProgress: true, - LatestStakedNode: latestStakedNode, - LatestStakedNodeHash: latestStakedNodeHash, + LatestStakedNode: latestStakedNodeNum, + LatestStakedNodeHash: latestStakedNodeInfo.NodeHash, StakerInfo: rawInfo, StakeExists: rawInfo != nil, } @@ -256,7 +261,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { return nil, err } if !nodesLinear { - log.Warn("fork detected") + log.Warn("rollup assertion fork detected") if effectiveStrategy == DefensiveStrategy { effectiveStrategy = StakeLatestStrategy } @@ -336,14 +341,13 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { } } - if rawInfo != nil && s.builder.BuilderTransactionCount() == 0 { + if rawInfo != nil && s.builder.BuildingTransactionCount() == 0 { if err := s.createConflict(ctx, rawInfo); err != nil { return nil, err } } - txCount := s.builder.BuilderTransactionCount() - if txCount == 0 { + if s.builder.BuildingTransactionCount() == 0 { return nil, nil } @@ -523,37 +527,37 @@ func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { if stakerInfo.CurrentChallenge != nil { continue } - conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(callOpts, s.rollupAddress, walletAddr, staker, big.NewInt(1024)) + conflictInfo, err := s.validatorUtils.FindStakerConflict(callOpts, s.rollupAddress, walletAddr, staker, big.NewInt(1024)) if err != nil { return err } - if ConflictType(conflictType) != CONFLICT_TYPE_FOUND { + if ConflictType(conflictInfo.Ty) != CONFLICT_TYPE_FOUND { continue } staker1 := walletAddr staker2 := staker - if node2 < node1 { + if conflictInfo.Node2 < conflictInfo.Node1 { staker1, staker2 = staker2, staker1 - node1, node2 = node2, node1 + conflictInfo.Node1, conflictInfo.Node2 = conflictInfo.Node2, conflictInfo.Node1 } - if node1 <= latestNode { + if conflictInfo.Node1 <= latestNode { // removeOldStakers will take care of them continue } - node1Info, err := s.rollup.LookupNode(ctx, node1) + node1Info, err := s.rollup.LookupNode(ctx, conflictInfo.Node1) if err != nil { return err } - node2Info, err := s.rollup.LookupNode(ctx, node2) + node2Info, err := s.rollup.LookupNode(ctx, conflictInfo.Node2) if err != nil { return err } - log.Warn("creating challenge", "ourNode", node1, "otherNode", node2, "otherStaker", staker2) + log.Warn("creating challenge", "node1", conflictInfo.Node1, "node2", conflictInfo.Node2, "otherStaker", staker2) _, err = s.rollup.CreateChallenge( s.builder.Auth(ctx), [2]common.Address{staker1, staker2}, - [2]uint64{node1, node2}, + [2]uint64{conflictInfo.Node1, conflictInfo.Node2}, node1Info.MachineStatuses(), node1Info.GlobalStates(), node1Info.Assertion.NumBlocks, diff --git a/validator/validator_utils.go b/validator/validator_utils.go deleted file mode 100644 index 53ed7a2cd6..0000000000 --- a/validator/validator_utils.go +++ /dev/null @@ -1,162 +0,0 @@ -// -// Copyright 2021, Offchain Labs, Inc. All rights reserved. -// - -package validator - -import ( - "context" - "math/big" - - "github.com/offchainlabs/arbstate/arbutil" - "github.com/offchainlabs/arbstate/solgen/go/rollupgen" - "github.com/pkg/errors" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - common "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" -) - -type ConfirmType uint8 - -const ( - CONFIRM_TYPE_NONE ConfirmType = iota - CONFIRM_TYPE_VALID - CONFIRM_TYPE_INVALID -) - -type ConflictType uint8 - -const ( - CONFLICT_TYPE_NONE ConflictType = iota - CONFLICT_TYPE_FOUND - CONFLICT_TYPE_INDETERMINATE - CONFLICT_TYPE_INCOMPLETE -) - -type ValidatorUtils struct { - con *rollupgen.ValidatorUtils - client arbutil.L1Interface - address common.Address - rollupAddress common.Address - baseCallOpts bind.CallOpts -} - -func NewValidatorUtils(address, rollupAddress common.Address, client arbutil.L1Interface, callOpts bind.CallOpts) (*ValidatorUtils, error) { - con, err := rollupgen.NewValidatorUtils(address, client) - if err != nil { - return nil, errors.WithStack(err) - } - - return &ValidatorUtils{ - con: con, - client: client, - address: address, - rollupAddress: rollupAddress, - baseCallOpts: callOpts, - }, nil -} - -func (v *ValidatorUtils) getCallOpts(ctx context.Context) *bind.CallOpts { - opts := v.baseCallOpts - opts.Context = ctx - return &opts -} - -func (v *ValidatorUtils) RefundableStakers(ctx context.Context) ([]common.Address, error) { - addresses, err := v.con.RefundableStakers(v.getCallOpts(ctx), v.rollupAddress) - if err != nil { - return nil, errors.WithStack(err) - } - return addresses, nil -} - -func (v *ValidatorUtils) TimedOutChallenges(ctx context.Context, max int) ([]uint64, error) { - var count uint64 = 1024 - addresses := make([]uint64, 0) - for i := uint64(0); ; i += count { - newIndexes, hasMore, err := v.con.TimedOutChallenges(v.getCallOpts(ctx), v.rollupAddress, i, count) - addresses = append(addresses, newIndexes...) - if err != nil { - return nil, errors.WithStack(err) - } - if !hasMore { - break - } - if len(addresses) >= max { - break - } - } - if len(addresses) > max { - addresses = addresses[:max] - } - return addresses, nil -} - -type RollupConfig struct { - ConfirmPeriodBlocks *big.Int - ExtraChallengeTimeBlocks *big.Int - ArbGasSpeedLimitPerBlock *big.Int - BaseStake *big.Int - StakeToken common.Address -} - -func (v *ValidatorUtils) GetStakers(ctx context.Context) ([]common.Address, error) { - addresses, _, err := v.con.GetStakers(v.getCallOpts(ctx), v.rollupAddress, 0, ^uint64(0)) - if err != nil { - return nil, errors.WithStack(err) - } - return addresses, nil -} - -func (v *ValidatorUtils) LatestStaked(ctx context.Context, staker common.Address) (uint64, [32]byte, error) { - amount, hash, err := v.con.LatestStaked(v.getCallOpts(ctx), v.rollupAddress, staker) - return amount, hash, errors.WithStack(err) -} - -func (v *ValidatorUtils) StakedNodes(ctx context.Context, staker common.Address) ([]uint64, error) { - nodes, err := v.con.StakedNodes(v.getCallOpts(ctx), v.rollupAddress, staker) - return nodes, errors.WithStack(err) -} - -func (v *ValidatorUtils) AreUnresolvedNodesLinear(ctx context.Context) (bool, error) { - linear, err := v.con.AreUnresolvedNodesLinear(v.getCallOpts(ctx), v.rollupAddress) - return linear, errors.WithStack(err) -} - -func (v *ValidatorUtils) CheckDecidableNextNode(ctx context.Context) (ConfirmType, error) { - confirmType, err := v.con.CheckDecidableNextNode( - v.getCallOpts(ctx), - v.rollupAddress, - ) - if err != nil { - return CONFIRM_TYPE_NONE, errors.WithStack(err) - } - return ConfirmType(confirmType), nil -} - -func (v *ValidatorUtils) FindStakerConflict(ctx context.Context, staker1, staker2 common.Address) (ConflictType, uint64, uint64, error) { - conflictType, staker1Node, staker2Node, err := v.con.FindStakerConflict( - v.getCallOpts(ctx), - v.rollupAddress, - staker1, - staker2, - math.MaxBig256, - ) - if err != nil { - return CONFLICT_TYPE_NONE, 0, 0, errors.WithStack(err) - } - for ConflictType(conflictType) == CONFLICT_TYPE_INCOMPLETE { - conflictType, staker1Node, staker2Node, err = v.con.FindNodeConflict( - v.getCallOpts(ctx), - v.rollupAddress, - staker1Node, - staker2Node, - math.MaxBig256, - ) - if err != nil { - return CONFLICT_TYPE_NONE, 0, 0, errors.WithStack(err) - } - } - return ConflictType(conflictType), staker1Node, staker2Node, nil -} diff --git a/validator/validator_wallet.go b/validator/validator_wallet.go index 8b3513aa55..6b3bbd9d06 100644 --- a/validator/validator_wallet.go +++ b/validator/validator_wallet.go @@ -127,7 +127,7 @@ func combineTxes(txes []*types.Transaction) ([][]byte, []common.Address, []*big. } // Not thread safe! Don't call this from multiple threads at the same time. -func (v *ValidatorWallet) ExecuteTransactions(ctx context.Context, builder *BuilderBackend) (*types.Transaction, error) { +func (v *ValidatorWallet) ExecuteTransactions(ctx context.Context, builder *ValidatorTxBuilder) (*types.Transaction, error) { txes := builder.transactions if len(txes) == 0 { return nil, nil From 8d1c4cfa2e7f5ffe6ddfbbc445bb007e2795d364 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 00:12:21 -0600 Subject: [PATCH 091/110] Add comment for ValidatorTxBuilder --- validator/builder_backend.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/validator/builder_backend.go b/validator/builder_backend.go index dd0839fd37..5c24bf8ab2 100644 --- a/validator/builder_backend.go +++ b/validator/builder_backend.go @@ -17,6 +17,11 @@ import ( "github.com/pkg/errors" ) +// ValidatorTxBuilder combines any transactions sent to it via SendTransaction into one batch, +// which is then sent to the validator wallet. +// This lets the validator make multiple atomic transactions. +// This inherits from an eth client so it can be used as an L1Interface, +// where it transparently intercepts calls to SendTransaction and queues them for the next batch. type ValidatorTxBuilder struct { arbutil.L1Interface transactions []*types.Transaction From 4091371283316a00ffdf2838b3a268336efd36c7 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 11:08:16 -0600 Subject: [PATCH 092/110] Simplify HashProofHelper by storing proofs in a map instead of an array --- solgen/src/osp/HashProofHelper.sol | 36 +++++++----------------------- solgen/test/hash-proofs.ts | 26 ++++++++++++--------- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/solgen/src/osp/HashProofHelper.sol b/solgen/src/osp/HashProofHelper.sol index 5cc4af564e..3ffee6707a 100644 --- a/solgen/src/osp/HashProofHelper.sol +++ b/solgen/src/osp/HashProofHelper.sol @@ -4,12 +4,6 @@ pragma solidity ^0.8.0; import "../libraries/CryptographyPrimitives.sol"; contract HashProofHelper { - struct PreimagePart { - bytes32 fullHash; - uint64 offset; - bytes part; - } - struct KeccakState { uint64 offset; bytes part; @@ -17,21 +11,20 @@ contract HashProofHelper { uint256 length; } - PreimagePart[] public preimageParts; + mapping(bytes32 => mapping(uint64 => bytes)) public preimageParts; mapping(address => KeccakState) public keccakStates; event PreimagePartProven( bytes32 indexed fullHash, uint64 indexed offset, - uint256 indexed proofNumber, bytes part ); uint256 constant MAX_PART_LENGTH = 32; uint256 constant KECCAK_ROUND_INPUT = 136; - function proveWithFullPreimage(bytes calldata data, uint64 offset) external returns (uint256 proofNumber) { - bytes32 fullHash = keccak256(data); + function proveWithFullPreimage(bytes calldata data, uint64 offset) external returns (bytes32 fullHash) { + fullHash = keccak256(data); bytes memory part; if (data.length > offset) { uint256 partLength = data.length - offset; @@ -40,16 +33,10 @@ contract HashProofHelper { } part = data[offset:(offset + partLength)]; } - proofNumber = preimageParts.length; - preimageParts.push(PreimagePart({ - fullHash: fullHash, - offset: offset, - part: part - })); + preimageParts[fullHash][offset] = part; emit PreimagePartProven( fullHash, offset, - proofNumber, part ); } @@ -57,7 +44,7 @@ contract HashProofHelper { // Flags: a bitset signaling various things about the proof, ordered from least to most significant bits. // 0th bit: indicates that this data is the final chunk of preimage data. // 1st bit: indicates that the preimage part currently being built should be cleared before this. - function proveWithSplitPreimage(bytes calldata data, uint64 offset, uint256 flags) external returns (uint256 proofNumber) { + function proveWithSplitPreimage(bytes calldata data, uint64 offset, uint256 flags) external returns (bytes32 fullHash) { bool isFinal = (flags & (1 << 0)) != 0; if ((flags & (1 << 1)) != 0) { delete keccakStates[msg.sender]; @@ -85,9 +72,8 @@ contract HashProofHelper { } } if (!isFinal) { - return 0; + return bytes32(0); } - bytes32 fullHash; for (uint256 i = 0; i < 32; i++) { uint256 stateIdx = i / 8; // work around our weird keccakF function state ordering @@ -95,19 +81,13 @@ contract HashProofHelper { uint8 b = uint8(state.state[stateIdx] >> ((i % 8) * 8)); fullHash |= bytes32(uint256(b) << (248 - (i * 8))); } - proofNumber = preimageParts.length; - preimageParts.push(PreimagePart({ - fullHash: fullHash, - offset: state.offset, - part: state.part - })); - delete keccakStates[msg.sender]; + preimageParts[fullHash][state.offset] = state.part; emit PreimagePartProven( fullHash, state.offset, - proofNumber, state.part ); + delete keccakStates[msg.sender]; } function keccakUpdate(KeccakState storage state, bytes calldata data, bool isFinal) internal { diff --git a/solgen/test/hash-proofs.ts b/solgen/test/hash-proofs.ts index 7f09f2342e..8138dfecb8 100644 --- a/solgen/test/hash-proofs.ts +++ b/solgen/test/hash-proofs.ts @@ -16,15 +16,17 @@ describe("HashProofHelper", function () { const proofTx = await hashProofHelper.proveWithFullPreimage(bytes, offset); const receipt = await proofTx.wait(); - const proof = await hashProofHelper.preimageParts(receipt.logs[0].topics[3]); + const log = hashProofHelper.interface.parseLog(receipt.logs[0]); + const provenPart = await hashProofHelper.preimageParts(hash, offset); let dataHex = data.toString(16); dataHex = "00".slice(dataHex.length) + dataHex; const partLen = Math.min(32, Math.max(0, len - offset)); const partString = "0x" + dataHex.repeat(partLen); - assert.equal(proof[0], hash); - assert.equal(proof[1].toNumber(), offset); - assert.equal(proof[2], partString); + assert.equal(log.args["fullHash"], hash); + assert.equal(log.args["offset"], offset); + assert.equal(log.args["part"], partString); + assert.equal(provenPart, partString); } }); @@ -41,8 +43,9 @@ describe("HashProofHelper", function () { const hash = ethers.utils.keccak256(bytes); let provenLen = 0; - let proof = null; - while (proof === null) { + let provenPart = null; + let log = null; + while (provenPart === null) { let nextPartialLen = 136 * (1 + Math.floor(Math.random() * 2)); if (nextPartialLen > len - provenLen) { nextPartialLen = len - provenLen; @@ -52,7 +55,8 @@ describe("HashProofHelper", function () { const proofTx = await hashProofHelper.proveWithSplitPreimage(bytes.slice(provenLen, newProvenLen), offset, isFinal ? 1 : 0); const receipt = await proofTx.wait(); if (receipt.logs.length > 0) { - proof = await hashProofHelper.preimageParts(receipt.logs[0].topics[3]); + log = hashProofHelper.interface.parseLog(receipt.logs[0]); + provenPart = await hashProofHelper.preimageParts(hash, offset); } provenLen = newProvenLen; } @@ -61,9 +65,11 @@ describe("HashProofHelper", function () { dataHex = "00".slice(dataHex.length) + dataHex; const partLen = Math.min(32, Math.max(0, len - offset)); const partString = "0x" + dataHex.repeat(partLen); - assert.equal(proof[0], hash); - assert.equal(proof[1].toNumber(), offset); - assert.equal(proof[2], partString); + assert.isNotNull(log); + assert.equal(log!.args["fullHash"], hash); + assert.equal(log!.args["offset"], offset); + assert.equal(log!.args["part"], partString); + assert.equal(provenPart, partString); } }); }); From 6a2a2b8e9f5895321106ef65966047a9c1e1b27a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 11:17:13 -0600 Subject: [PATCH 093/110] Actually require that the preimage part is proven --- solgen/src/osp/HashProofHelper.sol | 29 ++++++++++++++++++++++++++--- solgen/test/hash-proofs.ts | 4 ++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/solgen/src/osp/HashProofHelper.sol b/solgen/src/osp/HashProofHelper.sol index 3ffee6707a..cff01dd89a 100644 --- a/solgen/src/osp/HashProofHelper.sol +++ b/solgen/src/osp/HashProofHelper.sol @@ -3,6 +3,9 @@ pragma solidity ^0.8.0; import "../libraries/CryptographyPrimitives.sol"; +/// @dev The requested hash preimage at the given offset has not been proven yet +error NotProven(bytes32 fullHash, uint64 offset); + contract HashProofHelper { struct KeccakState { uint64 offset; @@ -11,7 +14,12 @@ contract HashProofHelper { uint256 length; } - mapping(bytes32 => mapping(uint64 => bytes)) public preimageParts; + struct PreimagePart { + bool proven; + bytes part; + } + + mapping(bytes32 => mapping(uint64 => PreimagePart)) private preimageParts; mapping(address => KeccakState) public keccakStates; event PreimagePartProven( @@ -33,7 +41,10 @@ contract HashProofHelper { } part = data[offset:(offset + partLength)]; } - preimageParts[fullHash][offset] = part; + preimageParts[fullHash][offset] = PreimagePart({ + proven: true, + part: part + }); emit PreimagePartProven( fullHash, offset, @@ -81,7 +92,10 @@ contract HashProofHelper { uint8 b = uint8(state.state[stateIdx] >> ((i % 8) * 8)); fullHash |= bytes32(uint256(b) << (248 - (i * 8))); } - preimageParts[fullHash][state.offset] = state.part; + preimageParts[fullHash][state.offset] = PreimagePart({ + proven: true, + part: state.part + }); emit PreimagePartProven( fullHash, state.offset, @@ -132,4 +146,13 @@ contract HashProofHelper { function clearSplitProof() external { delete keccakStates[msg.sender]; } + + /// Retrieves up to 32 bytes of the preimage of fullHash at the given offset, reverting if it hasn't been proven yet. + function getPreimagePart(bytes32 fullHash, uint64 offset) external view returns (bytes memory) { + PreimagePart storage part = preimageParts[fullHash][offset]; + if (!part.proven) { + revert NotProven(fullHash, offset); + } + return part.part; + } } diff --git a/solgen/test/hash-proofs.ts b/solgen/test/hash-proofs.ts index 8138dfecb8..753ff4c780 100644 --- a/solgen/test/hash-proofs.ts +++ b/solgen/test/hash-proofs.ts @@ -17,7 +17,7 @@ describe("HashProofHelper", function () { const proofTx = await hashProofHelper.proveWithFullPreimage(bytes, offset); const receipt = await proofTx.wait(); const log = hashProofHelper.interface.parseLog(receipt.logs[0]); - const provenPart = await hashProofHelper.preimageParts(hash, offset); + const provenPart = await hashProofHelper.getPreimagePart(hash, offset); let dataHex = data.toString(16); dataHex = "00".slice(dataHex.length) + dataHex; @@ -56,7 +56,7 @@ describe("HashProofHelper", function () { const receipt = await proofTx.wait(); if (receipt.logs.length > 0) { log = hashProofHelper.interface.parseLog(receipt.logs[0]); - provenPart = await hashProofHelper.preimageParts(hash, offset); + provenPart = await hashProofHelper.getPreimagePart(hash, offset); } provenLen = newProvenLen; } From 3239438c7cf866784e7304dc7d919d72decbb82a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 11:27:09 -0600 Subject: [PATCH 094/110] Set CanProgress = false if DontChallenge prevents creating node --- validator/staker.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/staker.go b/validator/staker.go index e5b063f020..f5ade4e480 100644 --- a/validator/staker.go +++ b/validator/staker.go @@ -415,6 +415,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv case createNodeAction: if wrongNodesExist && s.config.DontChallenge { log.Error("refusing to challenge assertion as config disables challenges") + info.CanProgress = false return nil } if !active { From 9a5dc1a5b7868fcee285439bf17505d8d304440a Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 12:22:07 -0600 Subject: [PATCH 095/110] Fix WaitForTx timer --- arbutil/wait_for_l1.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index 8704aa0252..04385ed6fd 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -75,8 +75,6 @@ func WaitForTx(ctxinput context.Context, client L1Interface, txhash common.Hash, if checkInterval > time.Second { checkInterval = time.Second } - timer := time.NewTimer(checkInterval) - defer timer.Stop() for { receipt, err := client.TransactionReceipt(ctx, txhash) if err == nil && receipt != nil { @@ -93,13 +91,11 @@ func WaitForTx(ctxinput context.Context, client L1Interface, txhash common.Hash, return receipt, nil } } - if !timer.Stop() { - <-timer.C - } - timer.Reset(checkInterval) + // Note: time.After won't free the timer until after it expires. + // However, that's fine here, as checkInterval is at most a second. select { case <-chanHead: - case <-timer.C: + case <-time.After(checkInterval): case <-ctx.Done(): return nil, ctx.Err() } From 8821eb2e75993c931390dc5c3096cf9ceebfa183 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 13:15:03 -0600 Subject: [PATCH 096/110] Update geth submodule --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index 1e9c9b8613..b34151e7b2 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 1e9c9b86135dafebf7ab84641a5674e4249ee849 +Subproject commit b34151e7b2dfe148e6f8760e25ac011a92b964f7 From e608ddf85a4bc0393734b28d3b9072941aca17b8 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Tue, 22 Feb 2022 13:59:24 -0600 Subject: [PATCH 097/110] remove comment --- arbos/tx_processor.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 4dd90b09a9..a065d89655 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -351,8 +351,6 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { p.evm.StateDB.AddBalance(networkFeeAccount, computeCost) p.evm.StateDB.AddBalance(p.evm.Context.Coinbase, p.PosterFee) - /*p.evm.StateDB.AddBalance(p.tipRecipient, util.BigMul(p.tipWeiPerGas, gasUsed)) // tip on gas used - p.evm.StateDB.AddBalance(p.msg.From(), util.BigMulByUint(p.tipWeiPerGas, gasLeft)) // refund unused tip*/ if p.msg.GasPrice().Sign() > 0 { // in tests, gas price coud be 0 // ArbOS's gas pool is meant to enforce the computational speed-limit. From 38a0ff7cdac1e1b93dddf55b17c01471d9c26606 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Tue, 22 Feb 2022 16:49:16 -0600 Subject: [PATCH 098/110] remove Gas.md --- docs/arbos/Gas.md | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 docs/arbos/Gas.md diff --git a/docs/arbos/Gas.md b/docs/arbos/Gas.md deleted file mode 100644 index f93d70daa3..0000000000 --- a/docs/arbos/Gas.md +++ /dev/null @@ -1,9 +0,0 @@ -# Gas -TODO - -## Gas Estimating Retryables - -### NodeInterface.sol -To avoid creating new RPC methods for client-side tooling, nitro geth's [`InterceptRPCMessage`][InterceptRPCMessage_link] hook provides an opportunity to swap out the message its handling before deriving a transaction from it. ArbOS uses this hook to detect messages sent to the address `0xc8`, the location of a fictional contract ... TODO - -[InterceptRPCMessage_link]: todo From 1f1bb3a2eea13f34f6c779965d0c20a794a0188b Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sat, 19 Feb 2022 00:49:27 -0500 Subject: [PATCH 099/110] Setup solhint and prettier --- .gitignore | 1 + solgen/.solhint.json | 7 + solgen/package.json | 6 +- solgen/yarn.lock | 567 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 563 insertions(+), 18 deletions(-) create mode 100644 solgen/.solhint.json diff --git a/.gitignore b/.gitignore index 57ec4cf39a..49f05ded10 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ solgen/test/proofs/*.json /cmd/statetransfer/statetransfer /reproducible-wasm/*.wasm target/ +yarn-error.log diff --git a/solgen/.solhint.json b/solgen/.solhint.json new file mode 100644 index 0000000000..7b85860cfa --- /dev/null +++ b/solgen/.solhint.json @@ -0,0 +1,7 @@ +{ + "extends": "solhint:recommended", + "rules": { + "max-line-length": "off", + "compiler-version": "off" + } +} diff --git a/solgen/package.json b/solgen/package.json index 87cd94a426..dd11d75471 100644 --- a/solgen/package.json +++ b/solgen/package.json @@ -6,7 +6,8 @@ "author": "Offchain Labs, Inc.", "license": "Apache-2.0", "scripts": { - "build": "hardhat compile" + "build": "hardhat compile", + "solhint": "./node_modules/.bin/solhint -f table src/**/*.sol" }, "dependencies": { "@openzeppelin/contracts": "4.5.0", @@ -26,6 +27,9 @@ "chai": "^4.3.4", "ethereum-waffle": "^3.4.0", "ethers": "^5.5.2", + "prettier": "^2.5.1", + "prettier-plugin-solidity": "^1.0.0-beta.19", + "solhint": "^3.3.7", "solidity-coverage": "^0.7.17", "ts-node": "^10.4.0", "typechain": "^6.1.0", diff --git a/solgen/yarn.lock b/solgen/yarn.lock index 3bfb4dacd5..ce0421a779 100644 --- a/solgen/yarn.lock +++ b/solgen/yarn.lock @@ -2,6 +2,27 @@ # yarn lockfile v1 +"@babel/code-frame@^7.0.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -896,6 +917,13 @@ dependencies: antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -1203,11 +1231,21 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + acorn@^8.4.1: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" @@ -1240,7 +1278,7 @@ agent-base@6: dependencies: debug "4" -ajv@^6.12.3: +ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1265,6 +1303,11 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -1287,6 +1330,11 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -1306,6 +1354,11 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" @@ -1417,6 +1470,16 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" @@ -2342,6 +2405,30 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@~1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -2385,7 +2472,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2402,6 +2489,11 @@ chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -2488,6 +2580,18 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -2596,6 +2700,11 @@ command-line-usage@^6.1.0: table-layout "^1.0.1" typical "^5.2.0" +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + commander@3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" @@ -2702,6 +2811,16 @@ cors@^2.8.1: object-assign "^4" vary "^1" +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + crc-32@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" @@ -2830,7 +2949,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.3.2: +debug@^4.0.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -3009,6 +3128,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" @@ -3057,11 +3183,21 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emoji-regex@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.0.0.tgz#96559e19f82231b436403e059571241d627c42b8" + integrity sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + encode-utf8@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" @@ -3126,7 +3262,7 @@ errno@~0.1.1: dependencies: prr "~1.0.1" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -3204,6 +3340,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -3216,6 +3357,77 @@ escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -3226,11 +3438,35 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -3815,6 +4051,15 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -3851,6 +4096,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-glob@^3.0.3: version "3.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" @@ -3886,6 +4136,20 @@ fetch-ponyfill@^4.0.0: dependencies: node-fetch "~1.7.1" +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -3968,6 +4232,15 @@ find-yarn-workspace-root@^2.0.0: dependencies: micromatch "^4.0.2" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + flat@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.1.tgz#a392059cc382881ff98642f5da4dde0a959f309b" @@ -3975,6 +4248,11 @@ flat@^4.1.0: dependencies: is-buffer "~2.0.3" +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" @@ -4357,6 +4635,11 @@ global@~4.4.0: min-document "^2.19.0" process "^0.11.10" +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4724,7 +5007,7 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4750,6 +5033,11 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + ignore@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" @@ -4770,11 +5058,32 @@ immutable@^4.0.0-rc.12: resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + imul@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/imul/-/imul-1.0.1.tgz#9d5867161e8b3de96c2c38d5dc7cb102f35e2ac9" integrity sha1-nVhnFh6LPelsLDjV3HyxAvNeKsk= +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4798,6 +5107,25 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -4958,6 +5286,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -5002,6 +5335,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-function@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" @@ -5199,7 +5537,7 @@ js-sha3@0.8.0, js-sha3@^0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -"js-tokens@^3.0.0 || ^4.0.0": +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -5217,7 +5555,7 @@ js-yaml@3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@3.x: +js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -5245,6 +5583,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + json-rpc-engine@^3.4.0, json-rpc-engine@^3.6.0: version "3.8.0" resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz#9d4ff447241792e1d0a232f6ef927302bb0c62a9" @@ -5279,6 +5622,11 @@ json-schema@0.4.0: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -5612,7 +5960,7 @@ levelup@^4.3.2: level-supports "~1.0.0" xtend "~4.0.0" -levn@~0.3.0: +levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= @@ -5662,7 +6010,7 @@ lodash@4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4: +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5942,6 +6290,11 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mimic-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -6134,6 +6487,11 @@ murmur-128@^0.2.1: fmix "^0.1.0" imul "^1.0.0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" @@ -6156,6 +6514,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -6398,6 +6761,13 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + open@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -6406,7 +6776,7 @@ open@^7.4.2: is-docker "^2.0.0" is-wsl "^2.1.1" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -6514,6 +6884,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-asn1@^5.0.0, parse-asn1@^5.1.5: version "5.1.6" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" @@ -6537,6 +6914,14 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -6606,6 +6991,11 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -6713,7 +7103,24 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@^2.1.2: +prettier-plugin-solidity@^1.0.0-beta.19: + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== + dependencies: + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" + escape-string-regexp "^4.0.0" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.3" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.1.2, prettier@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== @@ -6738,6 +7145,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-to-callback@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" @@ -7062,6 +7474,11 @@ regexp.prototype.flags@^1.2.0: call-bind "^1.0.2" define-properties "^1.1.3" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" @@ -7151,6 +7568,16 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -7183,6 +7610,14 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + resumer@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759" @@ -7200,6 +7635,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^2.2.8, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -7222,6 +7664,11 @@ rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: dependencies: bn.js "^5.2.0" +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -7234,6 +7681,13 @@ rustbn.js@~0.2.0: resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7324,7 +7778,7 @@ semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7334,7 +7788,7 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.4: +semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== @@ -7464,6 +7918,11 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -7493,6 +7952,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -7563,6 +8031,33 @@ solc@^0.6.3: semver "^5.5.0" tmp "0.0.33" +solhint@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.7.tgz#b5da4fedf7a0fee954cb613b6c55a5a2b0063aa7" + integrity sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ== + dependencies: + "@solidity-parser/parser" "^0.14.1" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + solidity-coverage@^0.7.17: version "0.7.17" resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.17.tgz#5139de8f6666d4755d88f453d8e35632a7bb3444" @@ -7744,7 +8239,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2": +"string-width@^1.0.2 || 2", string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -7761,6 +8256,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.trim@~1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" @@ -7826,6 +8330,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -7845,7 +8356,7 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" -strip-json-comments@2.0.1: +strip-json-comments@2.0.1, strip-json-comments@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -7910,6 +8421,16 @@ table-layout@^1.0.1: typical "^5.2.0" wordwrapjs "^4.0.0" +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tape@^4.6.3: version "4.14.0" resolved "https://registry.yarnpkg.com/tape/-/tape-4.14.0.tgz#e4d46097e129817175b90925f2385f6b1bcfa826" @@ -7957,6 +8478,11 @@ testrpc@0.0.1: resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -7965,7 +8491,7 @@ through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" -through@~2.3.4, through@~2.3.8: +through@^2.3.6, through@~2.3.4, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -8117,7 +8643,7 @@ ts-node@^10.4.0: make-error "^1.1.1" yn "3.1.1" -tslib@^1.9.3: +tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -9107,6 +9633,13 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" From ba1264ad7adb59e74a55840957a2fd6314279048 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Sun, 20 Feb 2022 20:03:35 -0500 Subject: [PATCH 100/110] Add solhint and prettier for solidity --- solgen/.prettierrc | 5 +++++ solgen/.solhint.json | 14 ++++++++++---- solgen/package.json | 4 +++- solgen/yarn.lock | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 solgen/.prettierrc diff --git a/solgen/.prettierrc b/solgen/.prettierrc new file mode 100644 index 0000000000..0636572595 --- /dev/null +++ b/solgen/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "singleQuote": false, + "compiler": "0.8.6" +} diff --git a/solgen/.solhint.json b/solgen/.solhint.json index 7b85860cfa..bf20a59b2d 100644 --- a/solgen/.solhint.json +++ b/solgen/.solhint.json @@ -1,7 +1,13 @@ { - "extends": "solhint:recommended", + "extends": ["solhint:recommended"], "rules": { + "prettier/prettier": "error", + "avoid-throw": false, + "avoid-suicide": "error", + "avoid-sha3": "warn", "max-line-length": "off", - "compiler-version": "off" - } -} + "compiler-version": "off", + "func-visibility": ["warn",{"ignoreConstructors":true}] + }, + "plugins": ["prettier"] +} \ No newline at end of file diff --git a/solgen/package.json b/solgen/package.json index dd11d75471..99dafbb9c8 100644 --- a/solgen/package.json +++ b/solgen/package.json @@ -7,7 +7,8 @@ "license": "Apache-2.0", "scripts": { "build": "hardhat compile", - "solhint": "./node_modules/.bin/solhint -f table src/**/*.sol" + "solhint": "./node_modules/.bin/solhint -f table src/**/*.sol", + "prettier:solidity": "./node_modules/.bin/prettier --write src/**/*.sol" }, "dependencies": { "@openzeppelin/contracts": "4.5.0", @@ -30,6 +31,7 @@ "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.0.0-beta.19", "solhint": "^3.3.7", + "solhint-plugin-prettier": "^0.0.5", "solidity-coverage": "^0.7.17", "ts-node": "^10.4.0", "typechain": "^6.1.0", diff --git a/solgen/yarn.lock b/solgen/yarn.lock index ce0421a779..ce1e91adca 100644 --- a/solgen/yarn.lock +++ b/solgen/yarn.lock @@ -7103,6 +7103,13 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier-plugin-solidity@^1.0.0-beta.19: version "1.0.0-beta.19" resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" @@ -8031,6 +8038,13 @@ solc@^0.6.3: semver "^5.5.0" tmp "0.0.33" +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + solhint@^3.3.7: version "3.3.7" resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.7.tgz#b5da4fedf7a0fee954cb613b6c55a5a2b0063aa7" From d8d4cf35b1f7cc7d3af0f684b3a6cdfb5ad20f89 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Tue, 22 Feb 2022 21:04:14 -0500 Subject: [PATCH 101/110] Run solhint and add to makefile --- Makefile | 3 +- solgen/.prettierrc | 2 +- solgen/.solhint.json | 2 +- solgen/src/bridge/Bridge.sol | 17 +- solgen/src/bridge/IBridge.sol | 2 +- solgen/src/bridge/IInbox.sol | 2 +- solgen/src/bridge/IOutbox.sol | 7 +- solgen/src/bridge/ISequencerInbox.sol | 2 +- solgen/src/bridge/Inbox.sol | 42 +- solgen/src/bridge/Messages.sol | 6 +- solgen/src/bridge/Outbox.sol | 32 +- solgen/src/bridge/SequencerInbox.sol | 109 +- solgen/src/challenge/ChallengeLib.sol | 31 +- solgen/src/challenge/ChallengeManager.sol | 118 +-- solgen/src/challenge/IChallengeManager.sol | 14 +- .../challenge/IChallengeResultReceiver.sol | 6 +- solgen/src/libraries/AdminFallbackProxy.sol | 34 +- solgen/src/libraries/ArbitrumProxy.sol | 19 +- solgen/src/libraries/Constants.sol | 2 +- .../src/libraries/CryptographyPrimitives.sol | 10 +- solgen/src/libraries/DelegateCallAware.sol | 7 +- solgen/src/libraries/IGasRefunder.sol | 14 +- solgen/src/libraries/MerkleLib.sol | 4 +- .../SecondaryLogicUUPSUpgradeable.sol | 8 +- solgen/src/libraries/UUPSNotUpgradeable.sol | 12 +- solgen/src/mocks/BridgeStub.sol | 5 +- solgen/src/mocks/Counter.sol | 8 +- solgen/src/mocks/ExecutionManager.sol | 13 +- solgen/src/mocks/InboxStub.sol | 23 +- solgen/src/mocks/MockResultReceiver.sol | 79 +- solgen/src/mocks/SequencerInboxStub.sol | 20 +- solgen/src/osp/HashProofHelper.sol | 275 +++-- solgen/src/osp/IOneStepProver.sol | 6 +- solgen/src/osp/OneStepProofEntry.sol | 62 +- solgen/src/osp/OneStepProver0.sol | 944 ++++++++++-------- solgen/src/osp/OneStepProverHostIo.sol | 85 +- solgen/src/osp/OneStepProverMath.sol | 903 +++++++++-------- solgen/src/osp/OneStepProverMemory.sol | 63 +- solgen/src/precompiles/ArbAddressTable.sol | 73 +- solgen/src/precompiles/ArbAggregator.sol | 6 +- solgen/src/precompiles/ArbBLS.sol | 34 +- solgen/src/precompiles/ArbDebug.sol | 26 +- solgen/src/precompiles/ArbFunctionTable.sol | 19 +- solgen/src/precompiles/ArbGasInfo.sol | 65 +- solgen/src/precompiles/ArbOwner.sol | 13 +- solgen/src/precompiles/ArbOwnerPublic.sol | 6 +- solgen/src/precompiles/ArbRetryableTx.sol | 78 +- solgen/src/precompiles/ArbStatistics.sol | 12 +- solgen/src/precompiles/ArbSys.sol | 111 +- solgen/src/precompiles/ArbosTest.sol | 4 +- solgen/src/rollup/IRollupCore.sol | 6 +- solgen/src/rollup/IRollupLogic.sol | 21 +- solgen/src/rollup/RollupAdminLogic.sol | 42 +- solgen/src/rollup/RollupCore.sol | 147 +-- solgen/src/rollup/RollupCreator.sol | 41 +- solgen/src/rollup/RollupEventBridge.sol | 15 +- solgen/src/rollup/RollupLib.sol | 61 +- solgen/src/rollup/RollupUserLogic.sol | 165 +-- solgen/src/rollup/ValidatorUtils.sol | 37 +- solgen/src/rollup/ValidatorWallet.sol | 9 +- solgen/src/state/Deserialize.sol | 521 +++++----- solgen/src/state/GlobalState.sol | 55 +- solgen/src/state/Instructions.sol | 5 +- solgen/src/state/Machine.sol | 90 +- solgen/src/state/MerkleProof.sol | 132 ++- solgen/src/state/ModuleMemory.sol | 39 +- solgen/src/state/PcArray.sol | 56 +- solgen/src/state/PcStack.sol | 32 +- solgen/src/state/StackFrame.sol | 35 +- solgen/src/state/Value.sol | 113 +-- solgen/src/state/ValueArray.sol | 56 +- solgen/src/state/ValueStack.sol | 70 +- 72 files changed, 2687 insertions(+), 2499 deletions(-) diff --git a/Makefile b/Makefile index 8aa8a3e2bb..a604cdb8e0 100644 --- a/Makefile +++ b/Makefile @@ -253,9 +253,10 @@ solgen/test/proofs/%.json: arbitrator/prover/test-cases/%.wasm $(arbitrator_prov golangci-lint run --fix @touch $@ -.make/fmt: build-node-deps | .make +.make/fmt: build-node-deps .make/yarndeps | .make golangci-lint run --disable-all -E gofmt --fix cargo fmt --all --manifest-path arbitrator/Cargo.toml -- --check + yarn --cwd solgen prettier:solidity @touch $@ .make/test-go: $(go_source) build-node-deps test-go-deps | .make diff --git a/solgen/.prettierrc b/solgen/.prettierrc index 0636572595..55310b5eda 100644 --- a/solgen/.prettierrc +++ b/solgen/.prettierrc @@ -1,5 +1,5 @@ { - "printWidth": 120, + "printWidth": 100, "singleQuote": false, "compiler": "0.8.6" } diff --git a/solgen/.solhint.json b/solgen/.solhint.json index bf20a59b2d..b889fd8ed8 100644 --- a/solgen/.solhint.json +++ b/solgen/.solhint.json @@ -10,4 +10,4 @@ "func-visibility": ["warn",{"ignoreConstructors":true}] }, "plugins": ["prettier"] -} \ No newline at end of file +} diff --git a/solgen/src/bridge/Bridge.sol b/solgen/src/bridge/Bridge.sol index 69b26eb6be..1e71635c7d 100644 --- a/solgen/src/bridge/Bridge.sol +++ b/solgen/src/bridge/Bridge.sol @@ -60,7 +60,7 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { address sender, bytes32 messageDataHash ) external payable override returns (uint256) { - if(!allowedInboxesMap[msg.sender].allowed) revert NotInbox(msg.sender); + if (!allowedInboxesMap[msg.sender].allowed) revert NotInbox(msg.sender); return addMessageToAccumulator( kind, @@ -95,7 +95,16 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { prevAcc = inboxAccs[count - 1]; } inboxAccs.push(Messages.accumulateInboxMessage(prevAcc, messageHash)); - emit MessageDelivered(count, prevAcc, msg.sender, kind, sender, messageDataHash, baseFeeL1, blockTimestamp); + emit MessageDelivered( + count, + prevAcc, + msg.sender, + kind, + sender, + messageDataHash, + baseFeeL1, + blockTimestamp + ); return count; } @@ -104,12 +113,12 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { uint256 value, bytes calldata data ) external override returns (bool success, bytes memory returnData) { - if(!allowedOutboxesMap[msg.sender].allowed) revert NotOutbox(msg.sender); + if (!allowedOutboxesMap[msg.sender].allowed) revert NotOutbox(msg.sender); if (data.length > 0 && !to.isContract()) revert NotContract(to); address prevOutbox = activeOutbox; activeOutbox = msg.sender; // We set and reset active outbox around external call so activeOutbox remains valid during call - (success, returnData) = to.call{ value: value }(data); + (success, returnData) = to.call{value: value}(data); activeOutbox = prevOutbox; emit BridgeCallTriggered(msg.sender, to, value, data); } diff --git a/solgen/src/bridge/IBridge.sol b/solgen/src/bridge/IBridge.sol index 7595d4cc9c..cf7df6a3dc 100644 --- a/solgen/src/bridge/IBridge.sol +++ b/solgen/src/bridge/IBridge.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.4; -import { NotContract } from "../libraries/Error.sol"; +import {NotContract} from "../libraries/Error.sol"; /// @dev Thrown when an un-authorized address tries to access an only-inbox function /// @param sender The un-authorized sender diff --git a/solgen/src/bridge/IInbox.sol b/solgen/src/bridge/IInbox.sol index af61e3eb64..386ea18f89 100644 --- a/solgen/src/bridge/IInbox.sol +++ b/solgen/src/bridge/IInbox.sol @@ -7,7 +7,7 @@ pragma solidity ^0.8.4; import "./IBridge.sol"; import "./IMessageProvider.sol"; -import { AlreadyInit, NotOrigin, DataTooLarge } from "../libraries/Error.sol"; +import {AlreadyInit, NotOrigin, DataTooLarge} from "../libraries/Error.sol"; /// @dev The contract is paused, so cannot be paused error AlreadyPaused(); diff --git a/solgen/src/bridge/IOutbox.sol b/solgen/src/bridge/IOutbox.sol index 402ad2ec15..97798782fa 100644 --- a/solgen/src/bridge/IOutbox.sol +++ b/solgen/src/bridge/IOutbox.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.4; -import { AlreadyInit, NotRollup } from "../libraries/Error.sol"; +import {AlreadyInit, NotRollup} from "../libraries/Error.sol"; /// @dev The provided proof was too long /// @param proofLength The length of the too-long proof @@ -41,10 +41,7 @@ error AlreadySpent(uint256 index); error BridgeCallFailed(); interface IOutbox { - event SendRootUpdated( - bytes32 indexed blockHash, - bytes32 indexed outputRoot - ); + event SendRootUpdated(bytes32 indexed blockHash, bytes32 indexed outputRoot); event OutBoxTransactionExecuted( address indexed to, address indexed l2Sender, diff --git a/solgen/src/bridge/ISequencerInbox.sol b/solgen/src/bridge/ISequencerInbox.sol index a7d5de379e..8b675aa7c7 100644 --- a/solgen/src/bridge/ISequencerInbox.sol +++ b/solgen/src/bridge/ISequencerInbox.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.0; import "../libraries/IGasRefunder.sol"; -import { AlreadyInit, HadZeroInit, NotOrigin, DataTooLarge, NotRollup } from "../libraries/Error.sol"; +import {AlreadyInit, HadZeroInit, NotOrigin, DataTooLarge, NotRollup} from "../libraries/Error.sol"; interface ISequencerInbox { struct MaxTimeVariation { diff --git a/solgen/src/bridge/Inbox.sol b/solgen/src/bridge/Inbox.sol index f1f6c6e96b..e565c43977 100644 --- a/solgen/src/bridge/Inbox.sol +++ b/solgen/src/bridge/Inbox.sol @@ -11,31 +11,31 @@ import "./IBridge.sol"; import "./Messages.sol"; import "../libraries/AddressAliasHelper.sol"; import "../libraries/DelegateCallAware.sol"; -import { - L2_MSG, - L1MessageType_L2FundedByL1, - L1MessageType_submitRetryableTx, - L2MessageType_unsignedEOATx, - L2MessageType_unsignedContractTx +import { + L2_MSG, + L1MessageType_L2FundedByL1, + L1MessageType_submitRetryableTx, + L2MessageType_unsignedEOATx, + L2MessageType_unsignedContractTx } from "../libraries/MessageTypes.sol"; -import { MAX_DATA_SIZE } from "../libraries/Constants.sol"; +import {MAX_DATA_SIZE} from "../libraries/Constants.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "./Bridge.sol"; /** -* @title Inbox for user and contract originated messages -* @notice Messages created via this inbox are enqueued in the delayed accumulator -* to await inclusion in the SequencerInbox -*/ + * @title Inbox for user and contract originated messages + * @notice Messages created via this inbox are enqueued in the delayed accumulator + * to await inclusion in the SequencerInbox + */ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { IBridge public override bridge; modifier onlyOwner() { // whoevever owns the Bridge, also owns the Inbox. this is usually the rollup contract address bridgeOwner = Bridge(address(bridge)).owner(); - if(msg.sender != bridgeOwner) revert NotOwner(msg.sender, bridgeOwner); + if (msg.sender != bridgeOwner) revert NotOwner(msg.sender, bridgeOwner); _; } @@ -50,7 +50,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { } function initialize(IBridge _bridge) external initializer onlyDelegated { - if(address(bridge) != address(0)) revert AlreadyInit(); + if (address(bridge) != address(0)) revert AlreadyInit(); bridge = _bridge; __Pausable_init(); } @@ -59,7 +59,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { /// this is used to fix the storage slots function postUpgradeInit(IBridge _bridge) external onlyDelegated onlyProxyOwner { uint8 slotsToWipe = 3; - for(uint8 i = 0; i MAX_DATA_SIZE) revert DataTooLarge(messageData.length, MAX_DATA_SIZE); + if (msg.sender != tx.origin) revert NotOrigin(); + if (messageData.length > MAX_DATA_SIZE) + revert DataTooLarge(messageData.length, MAX_DATA_SIZE); uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData)); emit InboxMessageDeliveredFromOrigin(msgNum); return msgNum; @@ -96,7 +97,8 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { whenNotPaused returns (uint256) { - if(messageData.length > MAX_DATA_SIZE) revert DataTooLarge(messageData.length, MAX_DATA_SIZE); + if (messageData.length > MAX_DATA_SIZE) + revert DataTooLarge(messageData.length, MAX_DATA_SIZE); uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData)); emit InboxMessageDelivered(msgNum, messageData); return msgNum; @@ -261,7 +263,6 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { uint256 maxFeePerGas, bytes calldata data ) public payable virtual whenNotPaused returns (uint256) { - return _deliverMessage( L1MessageType_submitRetryableTx, @@ -333,7 +334,8 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { address _sender, bytes memory _messageData ) internal returns (uint256) { - if(_messageData.length > MAX_DATA_SIZE) revert DataTooLarge(_messageData.length, MAX_DATA_SIZE); + if (_messageData.length > MAX_DATA_SIZE) + revert DataTooLarge(_messageData.length, MAX_DATA_SIZE); uint256 msgNum = deliverToBridge(_kind, _sender, keccak256(_messageData)); emit InboxMessageDelivered(msgNum, _messageData); return msgNum; @@ -344,6 +346,6 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { address sender, bytes32 messageDataHash ) internal returns (uint256) { - return bridge.enqueueDelayedMessage{ value: msg.value }(kind, sender, messageDataHash); + return bridge.enqueueDelayedMessage{value: msg.value}(kind, sender, messageDataHash); } } diff --git a/solgen/src/bridge/Messages.sol b/solgen/src/bridge/Messages.sol index 8f471a5f9d..97446156b1 100644 --- a/solgen/src/bridge/Messages.sol +++ b/solgen/src/bridge/Messages.sol @@ -29,7 +29,11 @@ library Messages { ); } - function accumulateInboxMessage(bytes32 prevAcc, bytes32 message) internal pure returns (bytes32) { + function accumulateInboxMessage(bytes32 prevAcc, bytes32 message) + internal + pure + returns (bytes32) + { return keccak256(abi.encodePacked(prevAcc, message)); } } diff --git a/solgen/src/bridge/Outbox.sol b/solgen/src/bridge/Outbox.sol index f835febd39..d7380dd8bd 100644 --- a/solgen/src/bridge/Outbox.sol +++ b/solgen/src/bridge/Outbox.sol @@ -11,11 +11,11 @@ import "../libraries/MerkleLib.sol"; import "../libraries/DelegateCallAware.sol"; contract Outbox is DelegateCallAware, IOutbox { - address public rollup; // the rollup contract - IBridge public bridge; // the bridge contract + address public rollup; // the rollup contract + IBridge public bridge; // the bridge contract - mapping(uint256 => bool ) spent; // maps leaf number => if spent - mapping(bytes32 => bytes32) roots; // maps root hashes => L2 block hash + mapping(uint256 => bool) spent; // maps leaf number => if spent + mapping(bytes32 => bytes32) roots; // maps root hashes => L2 block hash struct L2ToL1Context { uint128 l2Block; @@ -31,13 +31,13 @@ contract Outbox is DelegateCallAware, IOutbox { uint128 public constant OUTBOX_VERSION = 2; function initialize(address _rollup, IBridge _bridge) external onlyDelegated { - if(rollup != address(0)) revert AlreadyInit(); + if (rollup != address(0)) revert AlreadyInit(); rollup = _rollup; bridge = _bridge; } function updateSendRoot(bytes32 root, bytes32 l2BlockHash) external override { - if(msg.sender != rollup) revert NotRollup(msg.sender, rollup); + if (msg.sender != rollup) revert NotRollup(msg.sender, rollup); roots[root] = l2BlockHash; emit SendRootUpdated(root, l2BlockHash); } @@ -134,14 +134,14 @@ contract Outbox is DelegateCallAware, IOutbox { uint256 index, bytes32 item ) internal returns (bytes32) { - if(proof.length >= 256) revert ProofTooLong(proof.length); - if(index >= 2**proof.length) revert PathNotMinimal(index, 2**proof.length); + if (proof.length >= 256) revert ProofTooLong(proof.length); + if (index >= 2**proof.length) revert PathNotMinimal(index, 2**proof.length); // Hash the leaf an extra time to prove it's a leaf bytes32 calcRoot = calculateMerkleRoot(proof, index, item); - if(roots[calcRoot] == bytes32(0)) revert UnknownRoot(calcRoot); + if (roots[calcRoot] == bytes32(0)) revert UnknownRoot(calcRoot); - if(spent[index]) revert AlreadySpent(index); + if (spent[index]) revert AlreadySpent(index); spent[index] = true; return bytes32(index); @@ -176,17 +176,7 @@ contract Outbox is DelegateCallAware, IOutbox { bytes calldata data ) public pure returns (bytes32) { return - keccak256( - abi.encodePacked( - l2Sender, - to, - l2Block, - l1Block, - l2Timestamp, - value, - data - ) - ); + keccak256(abi.encodePacked(l2Sender, to, l2Block, l1Block, l2Timestamp, value, data)); } function calculateMerkleRoot( diff --git a/solgen/src/bridge/SequencerInbox.sol b/solgen/src/bridge/SequencerInbox.sol index 74fdffbee4..2a140c6202 100644 --- a/solgen/src/bridge/SequencerInbox.sol +++ b/solgen/src/bridge/SequencerInbox.sol @@ -9,9 +9,9 @@ import "./IBridge.sol"; import "./ISequencerInbox.sol"; import "./Messages.sol"; -import { GasRefundEnabled, IGasRefunder } from "../libraries/IGasRefunder.sol"; +import {GasRefundEnabled, IGasRefunder} from "../libraries/IGasRefunder.sol"; import "../libraries/DelegateCallAware.sol"; -import { MAX_DATA_SIZE } from "../libraries/Constants.sol"; +import {MAX_DATA_SIZE} from "../libraries/Constants.sol"; /** * @title Accepts batches from the sequencer and adds them to the rollup inbox. @@ -25,9 +25,9 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox uint256 public totalDelayedMessagesRead; IBridge public delayedBridge; - + /// @dev The size of the batch header - uint256 constant public HEADER_LENGTH = 40; + uint256 public constant HEADER_LENGTH = 40; address public rollup; mapping(address => bool) public isBatchPoster; @@ -35,7 +35,7 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox function initialize( IBridge delayedBridge_, - address rollup_, + address rollup_, ISequencerInbox.MaxTimeVariation calldata maxTimeVariation_ ) external onlyDelegated { if (delayedBridge != IBridge(address(0))) revert AlreadyInit(); @@ -88,28 +88,28 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox messageDataHash ); // Can only force-include after the Sequencer-only window has expired. - if (l1BlockAndTime[0] + maxTimeVariation.delayBlocks >= block.number) revert ForceIncludeBlockTooSoon(); - if (l1BlockAndTime[1] + maxTimeVariation.delaySeconds >= block.timestamp) revert ForceIncludeTimeTooSoon(); + if (l1BlockAndTime[0] + maxTimeVariation.delayBlocks >= block.number) + revert ForceIncludeBlockTooSoon(); + if (l1BlockAndTime[1] + maxTimeVariation.delaySeconds >= block.timestamp) + revert ForceIncludeTimeTooSoon(); // Verify that message hash represents the last message sequence of delayed message to be included bytes32 prevDelayedAcc = 0; if (_totalDelayedMessagesRead > 1) { - prevDelayedAcc = delayedBridge.inboxAccs( - _totalDelayedMessagesRead - 2 - ); + prevDelayedAcc = delayedBridge.inboxAccs(_totalDelayedMessagesRead - 2); } - if (delayedBridge.inboxAccs(_totalDelayedMessagesRead - 1) != - Messages.accumulateInboxMessage(prevDelayedAcc, messageHash)) revert IncorrectMessagePreimage(); + if ( + delayedBridge.inboxAccs(_totalDelayedMessagesRead - 1) != + Messages.accumulateInboxMessage(prevDelayedAcc, messageHash) + ) revert IncorrectMessagePreimage(); - ( - bytes32 dataHash, - TimeBounds memory timeBounds - ) = formEmptyDataHash(_totalDelayedMessagesRead); - ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 afterAcc - ) = addSequencerL2BatchImpl(dataHash, _totalDelayedMessagesRead); + (bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash( + _totalDelayedMessagesRead + ); + (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( + dataHash, + _totalDelayedMessagesRead + ); emit SequencerBatchDelivered( inboxAccs.length - 1, beforeAcc, @@ -131,15 +131,14 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox if (msg.sender != tx.origin) revert NotOrigin(); if (!isBatchPoster[msg.sender]) revert NotBatchPoster(); if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber(); - ( - bytes32 dataHash, - TimeBounds memory timeBounds - ) = formDataHash(data, afterDelayedMessagesRead); - ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 afterAcc - ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead); + (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash( + data, + afterDelayedMessagesRead + ); + (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( + dataHash, + afterDelayedMessagesRead + ); emit SequencerBatchDelivered( inboxAccs.length - 1, beforeAcc, @@ -160,12 +159,14 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox if (!isBatchPoster[msg.sender] && msg.sender != rollup) revert NotBatchPoster(); if (inboxAccs.length != sequenceNumber) revert BadSequencerNumber(); - (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash(data, afterDelayedMessagesRead); - ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 afterAcc - ) = addSequencerL2BatchImpl(dataHash, afterDelayedMessagesRead); + (bytes32 dataHash, TimeBounds memory timeBounds) = formDataHash( + data, + afterDelayedMessagesRead + ); + (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( + dataHash, + afterDelayedMessagesRead + ); emit SequencerBatchDelivered( sequenceNumber, beforeAcc, @@ -178,7 +179,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox emit SequencerBatchData(sequenceNumber, data); } - function packHeader(uint256 afterDelayedMessagesRead) internal view returns (bytes memory, TimeBounds memory) { + function packHeader(uint256 afterDelayedMessagesRead) + internal + view + returns (bytes memory, TimeBounds memory) + { TimeBounds memory timeBounds = getTimeBounds(); bytes memory header = abi.encodePacked( timeBounds.minTimestamp, @@ -192,7 +197,11 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox return (header, timeBounds); } - function formDataHash(bytes calldata data, uint256 afterDelayedMessagesRead) internal view returns (bytes32, TimeBounds memory) { + function formDataHash(bytes calldata data, uint256 afterDelayedMessagesRead) + internal + view + returns (bytes32, TimeBounds memory) + { uint256 fullDataLen = HEADER_LENGTH + data.length; if (fullDataLen < HEADER_LENGTH) revert DataLengthOverflow(); if (fullDataLen > MAX_DATA_SIZE) revert DataTooLarge(fullDataLen, MAX_DATA_SIZE); @@ -209,16 +218,16 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox return (keccak256(fullData), timeBounds); } - - function formEmptyDataHash(uint256 afterDelayedMessagesRead) internal view returns (bytes32, TimeBounds memory) { + function formEmptyDataHash(uint256 afterDelayedMessagesRead) + internal + view + returns (bytes32, TimeBounds memory) + { (bytes memory header, TimeBounds memory timeBounds) = packHeader(afterDelayedMessagesRead); return (keccak256(header), timeBounds); } - function addSequencerL2BatchImpl( - bytes32 dataHash, - uint256 afterDelayedMessagesRead - ) + function addSequencerL2BatchImpl(bytes32 dataHash, uint256 afterDelayedMessagesRead) internal returns ( bytes32 beforeAcc, @@ -245,17 +254,15 @@ contract SequencerInbox is DelegateCallAware, GasRefundEnabled, ISequencerInbox return inboxAccs.length; } - function setMaxTimeVariation( - ISequencerInbox.MaxTimeVariation memory maxTimeVariation_ - ) external override { + function setMaxTimeVariation(ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) + external + override + { if (msg.sender != rollup) revert NotRollup(msg.sender, rollup); maxTimeVariation = maxTimeVariation_; } - function setIsBatchPoster(address addr, bool isBatchPoster_) - external - override - { + function setIsBatchPoster(address addr, bool isBatchPoster_) external override { if (msg.sender != rollup) revert NotRollup(msg.sender, rollup); isBatchPoster[addr] = isBatchPoster_; } diff --git a/solgen/src/challenge/ChallengeLib.sol b/solgen/src/challenge/ChallengeLib.sol index dbb2255da4..d49bf4221f 100644 --- a/solgen/src/challenge/ChallengeLib.sol +++ b/solgen/src/challenge/ChallengeLib.sol @@ -55,13 +55,8 @@ library ChallengeLib { startingValues[0] = ValueLib.newRefNull(); startingValues[1] = ValueLib.newI32(0); startingValues[2] = ValueLib.newI32(0); - ValueArray memory valuesArray = ValueArray({ - inner: startingValues - }); - ValueStack memory values = ValueStack({ - proved: valuesArray, - remainingHash: 0 - }); + ValueArray memory valuesArray = ValueArray({inner: startingValues}); + ValueStack memory values = ValueStack({proved: valuesArray, remainingHash: 0}); ValueStack memory internalStack; PcStack memory blocks; StackFrameWindow memory frameStack; @@ -87,10 +82,7 @@ library ChallengeLib { returns (bytes32) { if (status == MachineStatus.FINISHED) { - return - keccak256( - abi.encodePacked("Machine finished:", globalStateHash) - ); + return keccak256(abi.encodePacked("Machine finished:", globalStateHash)); } else if (status == MachineStatus.ERRORED) { return keccak256(abi.encodePacked("Machine errored:")); } else if (status == MachineStatus.TOO_FAR) { @@ -100,8 +92,11 @@ library ChallengeLib { } } - - function extractChallengeSegment(SegmentSelection calldata selection) internal pure returns (uint256 segmentStart, uint256 segmentLength) { + function extractChallengeSegment(SegmentSelection calldata selection) + internal + pure + returns (uint256 segmentStart, uint256 segmentLength) + { uint256 oldChallengeDegree = selection.oldSegments.length - 1; segmentLength = selection.oldSegmentsLength / oldChallengeDegree; // Intentionally done before challengeLength is potentially added to for the final segment @@ -116,10 +111,7 @@ library ChallengeLib { uint256 segmentsLength, bytes32[] memory segments ) internal pure returns (bytes32) { - return - keccak256( - abi.encodePacked(segmentsStart, segmentsLength, segments) - ); + return keccak256(abi.encodePacked(segmentsStart, segmentsLength, segments)); } function blockStateHash(MachineStatus status, bytes32 globalStateHash) @@ -130,10 +122,7 @@ library ChallengeLib { if (status == MachineStatus.FINISHED) { return keccak256(abi.encodePacked("Block state:", globalStateHash)); } else if (status == MachineStatus.ERRORED) { - return - keccak256( - abi.encodePacked("Block state, errored:", globalStateHash) - ); + return keccak256(abi.encodePacked("Block state, errored:", globalStateHash)); } else if (status == MachineStatus.TOO_FAR) { return keccak256(abi.encodePacked("Block state, too far:")); } else { diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index 28a5b3ddbb..b84c878e3e 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -19,7 +19,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { uint256 constant MAX_CHALLENGE_DEGREE = 40; uint64 public totalChallengesCreated; - mapping (uint256 => ChallengeLib.Challenge) public challenges; + mapping(uint256 => ChallengeLib.Challenge) public challenges; IChallengeResultReceiver public resultReceiver; @@ -27,7 +27,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { IBridge public delayedBridge; IOneStepProofEntry public osp; - function challengeInfo(uint64 challengeIndex) external view override returns (ChallengeLib.Challenge memory) { + function challengeInfo(uint64 challengeIndex) + external + view + override + returns (ChallengeLib.Challenge memory) + { return challenges[challengeIndex]; } @@ -38,11 +43,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require( challenge.challengeStateHash == - ChallengeLib.hashChallengeState( - selection.oldSegmentsStart, - selection.oldSegmentsLength, - selection.oldSegments - ), + ChallengeLib.hashChallengeState( + selection.oldSegmentsStart, + selection.oldSegmentsLength, + selection.oldSegments + ), "BIS_STATE" ); if ( @@ -92,8 +97,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ) external override returns (uint64) { require(msg.sender == address(resultReceiver), "ONLY_ROLLUP_CHAL"); bytes32[] memory segments = new bytes32[](2); - segments[0] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[0], startAndEndGlobalStates_[0].hash()); - segments[1] = ChallengeLib.blockStateHash(startAndEndMachineStatuses_[1], startAndEndGlobalStates_[1].hash()); + segments[0] = ChallengeLib.blockStateHash( + startAndEndMachineStatuses_[0], + startAndEndGlobalStates_[0].hash() + ); + segments[1] = ChallengeLib.blockStateHash( + startAndEndMachineStatuses_[1], + startAndEndGlobalStates_[1].hash() + ); uint64 challengeIndex = ++totalChallengesCreated; // The following is an assertion since it should never be possible, but it's an important invariant @@ -103,14 +114,14 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { // See validator/assertion.go ExecutionState RequiredBatches() for reasoning uint64 maxInboxMessagesRead = startAndEndGlobalStates_[1].getInboxPosition(); - if (startAndEndMachineStatuses_[1] == MachineStatus.ERRORED || startAndEndGlobalStates_[1].getPositionInMessage() > 0) { + if ( + startAndEndMachineStatuses_[1] == MachineStatus.ERRORED || + startAndEndGlobalStates_[1].getPositionInMessage() > 0 + ) { maxInboxMessagesRead++; } challenge.maxInboxMessages = maxInboxMessagesRead; - challenge.next = ChallengeLib.Participant({ - addr: asserter_, - timeLeft: asserterTimeLeft_ - }); + challenge.next = ChallengeLib.Participant({addr: asserter_, timeLeft: asserterTimeLeft_}); challenge.current = ChallengeLib.Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ @@ -123,12 +134,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { startAndEndGlobalStates_[0], startAndEndGlobalStates_[1] ); - completeBisection( - challengeIndex, - 0, - numBlocks, - segments - ); + completeBisection(challengeIndex, 0, numBlocks, segments); return challengeIndex; } @@ -142,7 +148,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { ChallengeLib.SegmentSelection calldata selection, bytes32[] calldata newSegments ) external takeTurn(challengeIndex, selection) { - (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment( + selection + ); require(challengeLength > 1, "TOO_SHORT"); { uint256 expectedDegree = challengeLength; @@ -152,18 +160,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require(newSegments.length == expectedDegree + 1, "WRONG_DEGREE"); } - requireValidBisection( - selection, - newSegments[0], - newSegments[newSegments.length - 1] - ); + requireValidBisection(selection, newSegments[0], newSegments[newSegments.length - 1]); - completeBisection( - challengeIndex, - challengeStart, - challengeLength, - newSegments - ); + completeBisection(challengeIndex, challengeStart, challengeLength, newSegments); } function challengeExecution( @@ -176,18 +175,13 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { require(numSteps <= OneStepProofEntryLib.MAX_STEPS, "CHALLENGE_TOO_LONG"); requireValidBisection( selection, - ChallengeLib.blockStateHash( - machineStatuses[0], - globalStateHashes[0] - ), - ChallengeLib.blockStateHash( - machineStatuses[1], - globalStateHashes[1] - ) + ChallengeLib.blockStateHash(machineStatuses[0], globalStateHashes[0]), + ChallengeLib.blockStateHash(machineStatuses[1], globalStateHashes[1]) ); ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; - (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); + (uint256 executionChallengeAtSteps, uint256 challengeLength) = ChallengeLib + .extractChallengeSegment(selection); require(challengeLength == 1, "TOO_LONG"); if (machineStatuses[0] != MachineStatus.FINISHED) { @@ -211,19 +205,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { globalStateHashes[0], challenge.wasmModuleRoot ); - segments[1] = ChallengeLib.getEndMachineHash( - machineStatuses[1], - globalStateHashes[1] - ); + segments[1] = ChallengeLib.getEndMachineHash(machineStatuses[1], globalStateHashes[1]); challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; - completeBisection( - challengeIndex, - 0, - numSteps, - segments - ); + completeBisection(challengeIndex, 0, numSteps, segments); emit ExecutionChallengeBegun(challengeIndex, executionChallengeAtSteps); } @@ -234,7 +220,9 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { bytes calldata proof ) external takeTurn(challengeIndex, selection) { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; - (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment(selection); + (uint256 challengeStart, uint256 challengeLength) = ChallengeLib.extractChallengeSegment( + selection + ); require(challengeLength == 1, "TOO_LONG"); bytes32 afterHash = osp.proveOneStep( @@ -244,7 +232,7 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { delayedBridge: delayedBridge }), challengeStart, - selection.oldSegments[selection.challengePosition], + selection.oldSegments[selection.challengePosition], proof ); require( @@ -271,7 +259,12 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { return challenges[challengeIndex].current.addr; } - function currentResponderTimeLeft(uint64 challengeIndex) public override view returns (uint256) { + function currentResponderTimeLeft(uint64 challengeIndex) + public + view + override + returns (uint256) + { return challenges[challengeIndex].current.timeLeft; } @@ -318,7 +311,11 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { function _nextWin(uint64 challengeIndex, ChallengeTerminationType reason) private { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; delete challenges[challengeIndex]; - resultReceiver.completeChallenge(challengeIndex, challenge.next.addr, challenge.current.addr); + resultReceiver.completeChallenge( + challengeIndex, + challenge.next.addr, + challenge.current.addr + ); emit ChallengeEnded(challengeIndex, reason); } @@ -327,12 +324,15 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { * state. It is assumed that wherever this function is consumed, the turn is then adjusted for the opposite party * to timeout. This is done as a safety measure so challenges can only be resolved by timeouts during mainnet beta. */ - function _currentWin(uint64 challengeIndex, ChallengeTerminationType /* reason */) private { + function _currentWin( + uint64 challengeIndex, + ChallengeTerminationType /* reason */ + ) private { ChallengeLib.Challenge storage challenge = challenges[challengeIndex]; challenge.challengeStateHash = bytes32(0); -// delete challenges[challengeIndex]; -// resultReceiver.completeChallenge(challengeIndex, challenge.current.addr, challenge.next.addr); -// emit ChallengeEnded(challengeIndex, reason); + // delete challenges[challengeIndex]; + // resultReceiver.completeChallenge(challengeIndex, challenge.current.addr, challenge.next.addr); + // emit ChallengeEnded(challengeIndex, reason); } } diff --git a/solgen/src/challenge/IChallengeManager.sol b/solgen/src/challenge/IChallengeManager.sol index b2e2876d07..42f3472967 100644 --- a/solgen/src/challenge/IChallengeManager.sol +++ b/solgen/src/challenge/IChallengeManager.sol @@ -18,7 +18,11 @@ interface IChallengeManager { CLEARED } - event InitiatedChallenge(uint64 indexed challengeIndex, GlobalState startState, GlobalState endState); + event InitiatedChallenge( + uint64 indexed challengeIndex, + GlobalState startState, + GlobalState endState + ); event Bisected( uint64 indexed challengeIndex, @@ -51,12 +55,18 @@ interface IChallengeManager { uint256 challengerTimeLeft_ ) external returns (uint64); - function challengeInfo(uint64 challengeIndex_) external view returns (ChallengeLib.Challenge memory); + function challengeInfo(uint64 challengeIndex_) + external + view + returns (ChallengeLib.Challenge memory); function currentResponder(uint64 challengeIndex) external view returns (address); + function isTimedOut(uint64 challengeIndex) external view returns (bool); + function currentResponderTimeLeft(uint64 challengeIndex_) external view returns (uint256); function clearChallenge(uint64 challengeIndex_) external; + function timeout(uint64 challengeIndex_) external; } diff --git a/solgen/src/challenge/IChallengeResultReceiver.sol b/solgen/src/challenge/IChallengeResultReceiver.sol index 07f386fd73..2082bd4b6d 100644 --- a/solgen/src/challenge/IChallengeResultReceiver.sol +++ b/solgen/src/challenge/IChallengeResultReceiver.sol @@ -2,5 +2,9 @@ pragma solidity ^0.8.0; interface IChallengeResultReceiver { - function completeChallenge(uint256 challengeIndex, address winner, address loser) external; + function completeChallenge( + uint256 challengeIndex, + address winner, + address loser + ) external; } diff --git a/solgen/src/libraries/AdminFallbackProxy.sol b/solgen/src/libraries/AdminFallbackProxy.sol index f9339f6123..97391449aa 100644 --- a/solgen/src/libraries/AdminFallbackProxy.sol +++ b/solgen/src/libraries/AdminFallbackProxy.sol @@ -23,16 +23,17 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/StorageSlot.sol"; - /// @notice An extension to OZ's ERC1967Upgrade implementation to support two logic contracts abstract contract DoubleLogicERC1967Upgrade is ERC1967Upgrade { using Address for address; // This is the keccak-256 hash of "eip1967.proxy.implementation.secondary" subtracted by 1 - bytes32 internal constant _IMPLEMENTATION_SECONDARY_SLOT = 0x2b1dbce74324248c222f0ec2d5ed7bd323cfc425b336f0253c5ccfda7265546d; + bytes32 internal constant _IMPLEMENTATION_SECONDARY_SLOT = + 0x2b1dbce74324248c222f0ec2d5ed7bd323cfc425b336f0253c5ccfda7265546d; // This is the keccak-256 hash of "eip1967.proxy.rollback.secondary" subtracted by 1 - bytes32 private constant _ROLLBACK_SECONDARY_SLOT = 0x49bd798cd84788856140a4cd5030756b4d08a9e4d55db725ec195f232d262a89; + bytes32 private constant _ROLLBACK_SECONDARY_SLOT = + 0x49bd798cd84788856140a4cd5030756b4d08a9e4d55db725ec195f232d262a89; /** * @dev Emitted when the secondary implementation is upgraded. @@ -50,7 +51,10 @@ abstract contract DoubleLogicERC1967Upgrade is ERC1967Upgrade { * @dev Stores a new address in the EIP1967 implementation slot. */ function _setSecondaryImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967: new secondary implementation is not a contract"); + require( + Address.isContract(newImplementation), + "ERC1967: new secondary implementation is not a contract" + ); StorageSlot.getAddressSlot(_IMPLEMENTATION_SECONDARY_SLOT).value = newImplementation; } @@ -97,7 +101,10 @@ abstract contract DoubleLogicERC1967Upgrade is ERC1967Upgrade { _setSecondaryImplementation(newImplementation); } else { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { - require(slot == _IMPLEMENTATION_SECONDARY_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); + require( + slot == _IMPLEMENTATION_SECONDARY_SLOT, + "ERC1967Upgrade: unsupported proxiableUUID" + ); } catch { revert("ERC1967Upgrade: new secondary implementation is not UUPS"); } @@ -106,7 +113,6 @@ abstract contract DoubleLogicERC1967Upgrade is ERC1967Upgrade { } } - /// @notice similar to TransparentUpgradeableProxy but allows the admin to fallback to a separate logic contract using DoubleLogicERC1967Upgrade /// @dev this follows the UUPS pattern for upgradeability - read more at https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.5.0/contracts/proxy#transparent-vs-uups-proxies contract AdminFallbackProxy is Proxy, DoubleLogicERC1967Upgrade { @@ -125,20 +131,20 @@ contract AdminFallbackProxy is Proxy, DoubleLogicERC1967Upgrade { address adminAddr ) payable { assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); - assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); - assert(_IMPLEMENTATION_SECONDARY_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation.secondary")) - 1)); + assert( + _IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1) + ); + assert( + _IMPLEMENTATION_SECONDARY_SLOT == + bytes32(uint256(keccak256("eip1967.proxy.implementation.secondary")) - 1) + ); _changeAdmin(adminAddr); _upgradeToAndCall(adminLogic, adminData, false); _upgradeSecondaryToAndCall(userLogic, userData, false); } /// @inheritdoc Proxy - function _implementation() - internal - view - override - returns (address) - { + function _implementation() internal view override returns (address) { require(msg.data.length >= 4, "NO_FUNC_SIG"); // if the sender is the proxy's admin, delegate to admin logic // if the admin is disabled (set to addr zero), all calls will be forwarded to user logic diff --git a/solgen/src/libraries/ArbitrumProxy.sol b/solgen/src/libraries/ArbitrumProxy.sol index ef457fdc6a..ddc70c38fe 100644 --- a/solgen/src/libraries/ArbitrumProxy.sol +++ b/solgen/src/libraries/ArbitrumProxy.sol @@ -22,14 +22,13 @@ import "./AdminFallbackProxy.sol"; import "../rollup/IRollupLogic.sol"; contract ArbitrumProxy is AdminFallbackProxy { - constructor( - Config memory config, - ContractDependencies memory connectedContracts - ) AdminFallbackProxy( - address(connectedContracts.rollupAdminLogic), - abi.encodeWithSelector(IRollupAdmin.initialize.selector, config, connectedContracts), - address(connectedContracts.rollupUserLogic), - abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken), - config.owner - ) {} + constructor(Config memory config, ContractDependencies memory connectedContracts) + AdminFallbackProxy( + address(connectedContracts.rollupAdminLogic), + abi.encodeWithSelector(IRollupAdmin.initialize.selector, config, connectedContracts), + address(connectedContracts.rollupUserLogic), + abi.encodeWithSelector(IRollupUserAbs.initialize.selector, config.stakeToken), + config.owner + ) + {} } diff --git a/solgen/src/libraries/Constants.sol b/solgen/src/libraries/Constants.sol index c7eafdf45b..dd39a2daac 100644 --- a/solgen/src/libraries/Constants.sol +++ b/solgen/src/libraries/Constants.sol @@ -21,4 +21,4 @@ pragma solidity ^0.8.4; // 90% of Geth's 128KB tx size limit, leaving ~13KB for proving uint256 constant MAX_DATA_SIZE = 117964; -uint64 constant NO_CHAL_INDEX = 0; \ No newline at end of file +uint64 constant NO_CHAL_INDEX = 0; diff --git a/solgen/src/libraries/CryptographyPrimitives.sol b/solgen/src/libraries/CryptographyPrimitives.sol index daf6d81a88..d5d075ef2d 100644 --- a/solgen/src/libraries/CryptographyPrimitives.sol +++ b/solgen/src/libraries/CryptographyPrimitives.sol @@ -298,9 +298,15 @@ library CryptographyPrimitives { uint32 maj; for (i = 0; i < 64; i++) { - s1 = rightRotate(state[4], 6) ^ rightRotate(state[4], 11) ^ rightRotate(state[4], 25); + s1 = + rightRotate(state[4], 6) ^ + rightRotate(state[4], 11) ^ + rightRotate(state[4], 25); temp1 = state[7] + s1 + ch(state[4], state[5], state[6]) + k[i] + w[i]; - s0 = rightRotate(state[0], 2) ^ rightRotate(state[0], 13) ^ rightRotate(state[0], 22); + s0 = + rightRotate(state[0], 2) ^ + rightRotate(state[0], 13) ^ + rightRotate(state[0], 22); maj = (state[0] & (state[1] ^ state[2])) ^ (state[1] & state[2]); temp2 = s0 + maj; diff --git a/solgen/src/libraries/DelegateCallAware.sol b/solgen/src/libraries/DelegateCallAware.sol index ea727895e6..3b6499c00b 100644 --- a/solgen/src/libraries/DelegateCallAware.sol +++ b/solgen/src/libraries/DelegateCallAware.sol @@ -3,10 +3,9 @@ // SPDX-License-Identifier: UNLICENSED // - pragma solidity ^0.8.0; -import { NotOwner } from "./Error.sol"; +import {NotOwner} from "./Error.sol"; /// @dev A stateless contract that allows you to infer if the current call has been delegated or not /// Pattern used here is from UUPS implementation by the OpenZeppelin team @@ -31,7 +30,7 @@ abstract contract DelegateCallAware { _; } - /// @dev Check that msg.sender is the current EIP 1967 proxy admin + /// @dev Check that msg.sender is the current EIP 1967 proxy admin modifier onlyProxyOwner() { // Storage slot with the admin of the proxy contract // This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1 @@ -40,7 +39,7 @@ abstract contract DelegateCallAware { assembly { admin := sload(slot) } - if(msg.sender != admin) revert NotOwner(msg.sender, admin); + if (msg.sender != admin) revert NotOwner(msg.sender, admin); _; } } diff --git a/solgen/src/libraries/IGasRefunder.sol b/solgen/src/libraries/IGasRefunder.sol index bae42512f6..7039b7b808 100644 --- a/solgen/src/libraries/IGasRefunder.sol +++ b/solgen/src/libraries/IGasRefunder.sol @@ -1,4 +1,4 @@ -// +// // Copyright 2021, Offchain Labs, Inc. All rights reserved. // SPDX-License-Identifier: UNLICENSED // @@ -22,11 +22,7 @@ abstract contract GasRefundEnabled { assembly { calldataSize := calldatasize() } - gasRefunder.onGasSpent( - spender, - startGasLeft - gasleft(), - calldataSize - ); + gasRefunder.onGasSpent(spender, startGasLeft - gasleft(), calldataSize); } } @@ -34,11 +30,7 @@ abstract contract GasRefundEnabled { uint256 startGasLeft = gasleft(); _; if (address(gasRefunder) != address(0)) { - gasRefunder.onGasSpent( - spender, - startGasLeft - gasleft(), - 0 - ); + gasRefunder.onGasSpent(spender, startGasLeft - gasleft(), 0); } } } diff --git a/solgen/src/libraries/MerkleLib.sol b/solgen/src/libraries/MerkleLib.sol index dd5ec3b99a..f846757786 100644 --- a/solgen/src/libraries/MerkleLib.sol +++ b/solgen/src/libraries/MerkleLib.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.4; -import { MerkleProofTooLong } from "./Error.sol"; +import {MerkleProofTooLong} from "./Error.sol"; library MerkleLib { function generateRoot(bytes32[] memory _hashes) internal pure returns (bytes32) { @@ -45,7 +45,7 @@ library MerkleLib { bytes32 item ) internal pure returns (bytes32) { uint256 proofItems = nodes.length; - if(proofItems > 256) revert MerkleProofTooLong(proofItems, 256); + if (proofItems > 256) revert MerkleProofTooLong(proofItems, 256); bytes32 h = item; for (uint256 i = 0; i < proofItems; i++) { if (route % 2 == 0) { diff --git a/solgen/src/libraries/SecondaryLogicUUPSUpgradeable.sol b/solgen/src/libraries/SecondaryLogicUUPSUpgradeable.sol index 872db9fc61..b90bff7ca1 100644 --- a/solgen/src/libraries/SecondaryLogicUUPSUpgradeable.sol +++ b/solgen/src/libraries/SecondaryLogicUUPSUpgradeable.sol @@ -18,7 +18,7 @@ pragma solidity ^0.8.0; -import { DoubleLogicERC1967Upgrade } from "./AdminFallbackProxy.sol"; +import {DoubleLogicERC1967Upgrade} from "./AdminFallbackProxy.sol"; import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; /// @notice An extension to OZ's UUPSUpgradeable contract to be used for handling UUPS upgrades with a DoubleLogicERC1967Upgrade proxy @@ -61,7 +61,11 @@ abstract contract SecondaryLogicUUPSUpgradeable is UUPSUpgradeable, DoubleLogicE * * Emits an {UpgradedSecondary} event. */ - function upgradeSecondaryToAndCall(address newImplementation, bytes memory data) external payable onlyProxy { + function upgradeSecondaryToAndCall(address newImplementation, bytes memory data) + external + payable + onlyProxy + { _authorizeSecondaryUpgrade(newImplementation); _upgradeSecondaryToAndCallUUPS(newImplementation, data, true); } diff --git a/solgen/src/libraries/UUPSNotUpgradeable.sol b/solgen/src/libraries/UUPSNotUpgradeable.sol index 12d8745b0d..228f1c4c92 100644 --- a/solgen/src/libraries/UUPSNotUpgradeable.sol +++ b/solgen/src/libraries/UUPSNotUpgradeable.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/interfaces/draft-IERC1822.sol"; -import { DoubleLogicERC1967Upgrade } from "./AdminFallbackProxy.sol"; +import {DoubleLogicERC1967Upgrade} from "./AdminFallbackProxy.sol"; /** * @dev UUPSUpgradeable by OpenZeppelin but not upgradeable. This is expected to be used on the secondary @@ -23,7 +23,10 @@ abstract contract UUPSNotUpgradeable is IERC1822Proxiable, DoubleLogicERC1967Upg */ modifier onlyProxy() { require(address(this) != __self, "Function must be called through delegatecall"); - require(_getSecondaryImplementation() == __self, "Function must be called through active proxy"); + require( + _getSecondaryImplementation() == __self, + "Function must be called through active proxy" + ); _; } @@ -32,7 +35,10 @@ abstract contract UUPSNotUpgradeable is IERC1822Proxiable, DoubleLogicERC1967Upg * callable on the implementing contract but not through proxies. */ modifier notDelegated() { - require(address(this) == __self, "UUPSNotUpgradeable: must not be called through delegatecall"); + require( + address(this) == __self, + "UUPSNotUpgradeable: must not be called through delegatecall" + ); _; } diff --git a/solgen/src/mocks/BridgeStub.sol b/solgen/src/mocks/BridgeStub.sol index 4dab14bf30..716477f424 100644 --- a/solgen/src/mocks/BridgeStub.sol +++ b/solgen/src/mocks/BridgeStub.sol @@ -103,7 +103,10 @@ contract BridgeStub is IBridge { } } - function setOutbox(address /* outbox */, bool /* enabled*/) external pure override { + function setOutbox( + address, /* outbox */ + bool /* enabled*/ + ) external pure override { revert("NOT_IMPLEMENTED"); } diff --git a/solgen/src/mocks/Counter.sol b/solgen/src/mocks/Counter.sol index 42099676eb..8d539ef311 100644 --- a/solgen/src/mocks/Counter.sol +++ b/solgen/src/mocks/Counter.sol @@ -6,9 +6,9 @@ pragma solidity ^0.8.0; contract Simple { - uint64 public counter; + uint64 public counter; - function increment() external { - counter++; - } + function increment() external { + counter++; + } } diff --git a/solgen/src/mocks/ExecutionManager.sol b/solgen/src/mocks/ExecutionManager.sol index 947cd0216e..82bd4c5867 100644 --- a/solgen/src/mocks/ExecutionManager.sol +++ b/solgen/src/mocks/ExecutionManager.sol @@ -25,10 +25,7 @@ contract SingleExecutionChallenge is ChallengeManager { segments[1] = startAndEndHashes[1]; bytes32 challengeStateHash = ChallengeLib.hashChallengeState(0, numSteps_, segments); challenge.challengeStateHash = challengeStateHash; - challenge.next = ChallengeLib.Participant({ - addr: asserter_, - timeLeft: asserterTimeLeft_ - }); + challenge.next = ChallengeLib.Participant({addr: asserter_, timeLeft: asserterTimeLeft_}); challenge.current = ChallengeLib.Participant({ addr: challenger_, timeLeft: challengerTimeLeft_ @@ -36,12 +33,6 @@ contract SingleExecutionChallenge is ChallengeManager { challenge.lastMoveTimestamp = block.timestamp; challenge.mode = ChallengeLib.ChallengeMode.EXECUTION; - emit Bisected( - challengeIndex, - challengeStateHash, - 0, - numSteps_, - segments - ); + emit Bisected(challengeIndex, challengeStateHash, 0, numSteps_, segments); } } diff --git a/solgen/src/mocks/InboxStub.sol b/solgen/src/mocks/InboxStub.sol index 4adc8b9af0..cc1790dbd2 100644 --- a/solgen/src/mocks/InboxStub.sol +++ b/solgen/src/mocks/InboxStub.sol @@ -11,11 +11,11 @@ import "../bridge/IBridge.sol"; import "../bridge/Messages.sol"; import "./BridgeStub.sol"; import { - L2_MSG, - L1MessageType_L2FundedByL1, - L1MessageType_submitRetryableTx, - L2MessageType_unsignedEOATx, - L2MessageType_unsignedContractTx + L2_MSG, + L1MessageType_L2FundedByL1, + L1MessageType_submitRetryableTx, + L2MessageType_unsignedEOATx, + L2MessageType_unsignedContractTx } from "../libraries/MessageTypes.sol"; contract InboxStub is IInbox { @@ -33,10 +33,7 @@ contract InboxStub is IInbox { * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input * @param messageData Data of the message being sent */ - function sendL2MessageFromOrigin(bytes calldata messageData) - external - returns (uint256) - { + function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256) { // solhint-disable-next-line avoid-tx-origin require(msg.sender == tx.origin, "origin only"); uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData)); @@ -49,11 +46,7 @@ contract InboxStub is IInbox { * @dev This method can be used to send any type of message that doesn't require L1 validation * @param messageData Data of the message being sent */ - function sendL2Message(bytes calldata messageData) - external - override - returns (uint256) - { + function sendL2Message(bytes calldata messageData) external override returns (uint256) { uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(messageData)); emit InboxMessageDelivered(msgNum, messageData); return msgNum; @@ -64,7 +57,7 @@ contract InboxStub is IInbox { address sender, bytes32 messageDataHash ) internal returns (uint256) { - return bridge.enqueueDelayedMessage{ value: msg.value }(kind, sender, messageDataHash); + return bridge.enqueueDelayedMessage{value: msg.value}(kind, sender, messageDataHash); } function sendUnsignedTransaction( diff --git a/solgen/src/mocks/MockResultReceiver.sol b/solgen/src/mocks/MockResultReceiver.sol index a7aabbe7d5..abfc7155ca 100644 --- a/solgen/src/mocks/MockResultReceiver.sol +++ b/solgen/src/mocks/MockResultReceiver.sol @@ -5,43 +5,52 @@ import "../challenge/IChallengeResultReceiver.sol"; import "../challenge/IChallengeManager.sol"; contract MockResultReceiver is IChallengeResultReceiver { - IChallengeManager manager; - address public winner; - address public loser; - uint256 public challengeIndex; + IChallengeManager manager; + address public winner; + address public loser; + uint256 public challengeIndex; - event ChallengeCompleted(uint256 indexed challengeIndex, address indexed winner, address indexed loser); + event ChallengeCompleted( + uint256 indexed challengeIndex, + address indexed winner, + address indexed loser + ); - constructor (IChallengeManager manager_) { - manager = manager_; - } + constructor(IChallengeManager manager_) { + manager = manager_; + } - function createChallenge( - bytes32 wasmModuleRoot_, - MachineStatus[2] calldata startAndEndMachineStatuses_, - GlobalState[2] calldata startAndEndGlobalStates_, - uint64 numBlocks, - address asserter_, - address challenger_, - uint256 asserterTimeLeft_, - uint256 challengerTimeLeft_ - ) external returns (uint64) { - return manager.createChallenge( - wasmModuleRoot_, - startAndEndMachineStatuses_, - startAndEndGlobalStates_, - numBlocks, - asserter_, - challenger_, - asserterTimeLeft_, - challengerTimeLeft_ - ); - } + function createChallenge( + bytes32 wasmModuleRoot_, + MachineStatus[2] calldata startAndEndMachineStatuses_, + GlobalState[2] calldata startAndEndGlobalStates_, + uint64 numBlocks, + address asserter_, + address challenger_, + uint256 asserterTimeLeft_, + uint256 challengerTimeLeft_ + ) external returns (uint64) { + return + manager.createChallenge( + wasmModuleRoot_, + startAndEndMachineStatuses_, + startAndEndGlobalStates_, + numBlocks, + asserter_, + challenger_, + asserterTimeLeft_, + challengerTimeLeft_ + ); + } - function completeChallenge(uint256 challengeIndex_, address winner_, address loser_) external override { - winner = winner_; - loser = loser_; - challengeIndex = challengeIndex_; - emit ChallengeCompleted(challengeIndex, winner_, loser_); - } + function completeChallenge( + uint256 challengeIndex_, + address winner_, + address loser_ + ) external override { + winner = winner_; + loser = loser_; + challengeIndex = challengeIndex_; + emit ChallengeCompleted(challengeIndex, winner_, loser_); + } } diff --git a/solgen/src/mocks/SequencerInboxStub.sol b/solgen/src/mocks/SequencerInboxStub.sol index 417f8ef9bf..1cc04023c6 100644 --- a/solgen/src/mocks/SequencerInboxStub.sol +++ b/solgen/src/mocks/SequencerInboxStub.sol @@ -8,7 +8,11 @@ pragma solidity ^0.8.0; import "../bridge/SequencerInbox.sol"; contract SequencerInboxStub is SequencerInbox { - constructor(IBridge delayedBridge_, address sequencer_, ISequencerInbox.MaxTimeVariation memory maxTimeVariation_) { + constructor( + IBridge delayedBridge_, + address sequencer_, + ISequencerInbox.MaxTimeVariation memory maxTimeVariation_ + ) { delayedBridge = delayedBridge_; rollup = msg.sender; maxTimeVariation = maxTimeVariation_; @@ -16,15 +20,11 @@ contract SequencerInboxStub is SequencerInbox { } function addInitMessage() external { - ( - bytes32 dataHash, - TimeBounds memory timeBounds - ) = formEmptyDataHash(0); - ( - bytes32 beforeAcc, - bytes32 delayedAcc, - bytes32 afterAcc - ) = addSequencerL2BatchImpl(dataHash, 0); + (bytes32 dataHash, TimeBounds memory timeBounds) = formEmptyDataHash(0); + (bytes32 beforeAcc, bytes32 delayedAcc, bytes32 afterAcc) = addSequencerL2BatchImpl( + dataHash, + 0 + ); emit SequencerBatchDelivered( inboxAccs.length - 1, beforeAcc, diff --git a/solgen/src/osp/HashProofHelper.sol b/solgen/src/osp/HashProofHelper.sol index 5cc4af564e..79cb00e9d8 100644 --- a/solgen/src/osp/HashProofHelper.sol +++ b/solgen/src/osp/HashProofHelper.sol @@ -4,152 +4,147 @@ pragma solidity ^0.8.0; import "../libraries/CryptographyPrimitives.sol"; contract HashProofHelper { - struct PreimagePart { - bytes32 fullHash; - uint64 offset; - bytes part; - } + struct PreimagePart { + bytes32 fullHash; + uint64 offset; + bytes part; + } - struct KeccakState { - uint64 offset; - bytes part; - uint64[25] state; - uint256 length; - } + struct KeccakState { + uint64 offset; + bytes part; + uint64[25] state; + uint256 length; + } - PreimagePart[] public preimageParts; - mapping(address => KeccakState) public keccakStates; + PreimagePart[] public preimageParts; + mapping(address => KeccakState) public keccakStates; - event PreimagePartProven( - bytes32 indexed fullHash, - uint64 indexed offset, - uint256 indexed proofNumber, - bytes part - ); + event PreimagePartProven( + bytes32 indexed fullHash, + uint64 indexed offset, + uint256 indexed proofNumber, + bytes part + ); - uint256 constant MAX_PART_LENGTH = 32; - uint256 constant KECCAK_ROUND_INPUT = 136; + uint256 constant MAX_PART_LENGTH = 32; + uint256 constant KECCAK_ROUND_INPUT = 136; - function proveWithFullPreimage(bytes calldata data, uint64 offset) external returns (uint256 proofNumber) { - bytes32 fullHash = keccak256(data); - bytes memory part; - if (data.length > offset) { - uint256 partLength = data.length - offset; - if (partLength > 32) { - partLength = 32; - } - part = data[offset:(offset + partLength)]; - } - proofNumber = preimageParts.length; - preimageParts.push(PreimagePart({ - fullHash: fullHash, - offset: offset, - part: part - })); - emit PreimagePartProven( - fullHash, - offset, - proofNumber, - part - ); - } + function proveWithFullPreimage(bytes calldata data, uint64 offset) + external + returns (uint256 proofNumber) + { + bytes32 fullHash = keccak256(data); + bytes memory part; + if (data.length > offset) { + uint256 partLength = data.length - offset; + if (partLength > 32) { + partLength = 32; + } + part = data[offset:(offset + partLength)]; + } + proofNumber = preimageParts.length; + preimageParts.push(PreimagePart({fullHash: fullHash, offset: offset, part: part})); + emit PreimagePartProven(fullHash, offset, proofNumber, part); + } - // Flags: a bitset signaling various things about the proof, ordered from least to most significant bits. - // 0th bit: indicates that this data is the final chunk of preimage data. - // 1st bit: indicates that the preimage part currently being built should be cleared before this. - function proveWithSplitPreimage(bytes calldata data, uint64 offset, uint256 flags) external returns (uint256 proofNumber) { - bool isFinal = (flags & (1 << 0)) != 0; - if ((flags & (1 << 1)) != 0) { - delete keccakStates[msg.sender]; - } - require(isFinal || data.length % KECCAK_ROUND_INPUT == 0, "NOT_BLOCK_ALIGNED"); - KeccakState storage state = keccakStates[msg.sender]; - uint256 startLength = state.length; - if (startLength == 0) { - state.offset = offset; - } else { - require(state.offset == offset, "DIFF_OFFSET"); - } - keccakUpdate(state, data, isFinal); - if (uint256(offset) + MAX_PART_LENGTH > startLength && offset < state.length) { - uint256 startIdx = 0; - if (offset > startLength) { - startIdx = offset - startLength; - } - uint256 endIdx = uint256(offset) + MAX_PART_LENGTH - startLength; - if (endIdx > data.length) { - endIdx = data.length; - } - for (uint256 i = startIdx; i < endIdx; i++) { - state.part.push(data[i]); - } - } - if (!isFinal) { - return 0; - } - bytes32 fullHash; - for (uint256 i = 0; i < 32; i++) { - uint256 stateIdx = i / 8; - // work around our weird keccakF function state ordering - stateIdx = 5 * (stateIdx % 5) + stateIdx / 5; - uint8 b = uint8(state.state[stateIdx] >> ((i % 8) * 8)); - fullHash |= bytes32(uint256(b) << (248 - (i * 8))); - } - proofNumber = preimageParts.length; - preimageParts.push(PreimagePart({ - fullHash: fullHash, - offset: state.offset, - part: state.part - })); - delete keccakStates[msg.sender]; - emit PreimagePartProven( - fullHash, - state.offset, - proofNumber, - state.part - ); - } + // Flags: a bitset signaling various things about the proof, ordered from least to most significant bits. + // 0th bit: indicates that this data is the final chunk of preimage data. + // 1st bit: indicates that the preimage part currently being built should be cleared before this. + function proveWithSplitPreimage( + bytes calldata data, + uint64 offset, + uint256 flags + ) external returns (uint256 proofNumber) { + bool isFinal = (flags & (1 << 0)) != 0; + if ((flags & (1 << 1)) != 0) { + delete keccakStates[msg.sender]; + } + require(isFinal || data.length % KECCAK_ROUND_INPUT == 0, "NOT_BLOCK_ALIGNED"); + KeccakState storage state = keccakStates[msg.sender]; + uint256 startLength = state.length; + if (startLength == 0) { + state.offset = offset; + } else { + require(state.offset == offset, "DIFF_OFFSET"); + } + keccakUpdate(state, data, isFinal); + if (uint256(offset) + MAX_PART_LENGTH > startLength && offset < state.length) { + uint256 startIdx = 0; + if (offset > startLength) { + startIdx = offset - startLength; + } + uint256 endIdx = uint256(offset) + MAX_PART_LENGTH - startLength; + if (endIdx > data.length) { + endIdx = data.length; + } + for (uint256 i = startIdx; i < endIdx; i++) { + state.part.push(data[i]); + } + } + if (!isFinal) { + return 0; + } + bytes32 fullHash; + for (uint256 i = 0; i < 32; i++) { + uint256 stateIdx = i / 8; + // work around our weird keccakF function state ordering + stateIdx = 5 * (stateIdx % 5) + stateIdx / 5; + uint8 b = uint8(state.state[stateIdx] >> ((i % 8) * 8)); + fullHash |= bytes32(uint256(b) << (248 - (i * 8))); + } + proofNumber = preimageParts.length; + preimageParts.push( + PreimagePart({fullHash: fullHash, offset: state.offset, part: state.part}) + ); + delete keccakStates[msg.sender]; + emit PreimagePartProven(fullHash, state.offset, proofNumber, state.part); + } - function keccakUpdate(KeccakState storage state, bytes calldata data, bool isFinal) internal { - state.length += data.length; - while (true) { - if (data.length == 0 && !isFinal) { - break; - } - for (uint256 i = 0; i < KECCAK_ROUND_INPUT; i++) { - uint8 b = 0; - if (i < data.length) { - b = uint8(data[i]); - } else { - // Padding - if (i == data.length) { - b |= uint8(0x01); - } - if (i == KECCAK_ROUND_INPUT-1) { - b |= uint8(0x80); - } - } - uint256 stateIdx = i / 8; - // work around our weird keccakF function state ordering - stateIdx = 5 * (stateIdx % 5) + stateIdx / 5; - state.state[stateIdx] ^= uint64(b) << uint64((i % 8) * 8); - } - uint256[25] memory state256; - for (uint256 i = 0; i < 25; i++) { - state256[i] = state.state[i]; - } - state256 = CryptographyPrimitives.keccakF(state256); - for (uint256 i = 0; i < 25; i++) { - state.state[i] = uint64(state256[i]); - } - if (data.length < KECCAK_ROUND_INPUT) { - break; - } - data = data[KECCAK_ROUND_INPUT:]; - } - } + function keccakUpdate( + KeccakState storage state, + bytes calldata data, + bool isFinal + ) internal { + state.length += data.length; + while (true) { + if (data.length == 0 && !isFinal) { + break; + } + for (uint256 i = 0; i < KECCAK_ROUND_INPUT; i++) { + uint8 b = 0; + if (i < data.length) { + b = uint8(data[i]); + } else { + // Padding + if (i == data.length) { + b |= uint8(0x01); + } + if (i == KECCAK_ROUND_INPUT - 1) { + b |= uint8(0x80); + } + } + uint256 stateIdx = i / 8; + // work around our weird keccakF function state ordering + stateIdx = 5 * (stateIdx % 5) + stateIdx / 5; + state.state[stateIdx] ^= uint64(b) << uint64((i % 8) * 8); + } + uint256[25] memory state256; + for (uint256 i = 0; i < 25; i++) { + state256[i] = state.state[i]; + } + state256 = CryptographyPrimitives.keccakF(state256); + for (uint256 i = 0; i < 25; i++) { + state.state[i] = uint64(state256[i]); + } + if (data.length < KECCAK_ROUND_INPUT) { + break; + } + data = data[KECCAK_ROUND_INPUT:]; + } + } - function clearSplitProof() external { - delete keccakStates[msg.sender]; - } + function clearSplitProof() external { + delete keccakStates[msg.sender]; + } } diff --git a/solgen/src/osp/IOneStepProver.sol b/solgen/src/osp/IOneStepProver.sol index 9b50b3a163..27dd834a6d 100644 --- a/solgen/src/osp/IOneStepProver.sol +++ b/solgen/src/osp/IOneStepProver.sol @@ -20,9 +20,5 @@ abstract contract IOneStepProver { Module calldata mod, Instruction calldata instruction, bytes calldata proof - ) - external - view - virtual - returns (Machine memory result, Module memory resultMod); + ) external view virtual returns (Machine memory result, Module memory resultMod); } diff --git a/solgen/src/osp/OneStepProofEntry.sol b/solgen/src/osp/OneStepProofEntry.sol index 50b699e8af..c1a1289b77 100644 --- a/solgen/src/osp/OneStepProofEntry.sol +++ b/solgen/src/osp/OneStepProofEntry.sol @@ -57,8 +57,7 @@ contract OneStepProofEntry is IOneStepProofEntry { (mod, offset) = Deserialize.module(proof, offset); (modProof, offset) = Deserialize.merkleProof(proof, offset); require( - modProof.computeRootFromModule(mach.moduleIdx, mod) == - mach.modulesRoot, + modProof.computeRootFromModule(mach.moduleIdx, mod) == mach.modulesRoot, "MODULES_ROOT" ); @@ -68,18 +67,12 @@ contract OneStepProofEntry is IOneStepProofEntry { (inst, offset) = Deserialize.instruction(proof, offset); (instProof, offset) = Deserialize.merkleProof(proof, offset); (funcProof, offset) = Deserialize.merkleProof(proof, offset); - bytes32 codeHash = instProof.computeRootFromInstruction( - mach.functionPc, - inst - ); + bytes32 codeHash = instProof.computeRootFromInstruction(mach.functionPc, inst); bytes32 recomputedRoot = funcProof.computeRootFromFunction( mach.functionIdx, codeHash ); - require( - recomputedRoot == mod.functionsMerkleRoot, - "BAD_FUNCTIONS_ROOT" - ); + require(recomputedRoot == mod.functionsMerkleRoot, "BAD_FUNCTIONS_ROOT"); } proof = proof[offset:]; } @@ -89,38 +82,27 @@ contract OneStepProofEntry is IOneStepProofEntry { uint16 opcode = inst.opcode; IOneStepProver prover; if ( - (opcode >= Instructions.I32_LOAD && - opcode <= Instructions.I64_LOAD32_U) || - (opcode >= Instructions.I32_STORE && - opcode <= Instructions.I64_STORE32) || + (opcode >= Instructions.I32_LOAD && opcode <= Instructions.I64_LOAD32_U) || + (opcode >= Instructions.I32_STORE && opcode <= Instructions.I64_STORE32) || opcode == Instructions.MEMORY_SIZE || opcode == Instructions.MEMORY_GROW ) { prover = proverMem; } else if ( - (opcode == Instructions.I32_EQZ || - opcode == Instructions.I64_EQZ) || + (opcode == Instructions.I32_EQZ || opcode == Instructions.I64_EQZ) || (opcode >= Instructions.I32_RELOP_BASE && - opcode <= - Instructions.I32_RELOP_BASE + Instructions.IRELOP_LAST) || + opcode <= Instructions.I32_RELOP_BASE + Instructions.IRELOP_LAST) || (opcode >= Instructions.I32_UNOP_BASE && - opcode <= - Instructions.I32_UNOP_BASE + Instructions.IUNOP_LAST) || - (opcode >= Instructions.I32_ADD && - opcode <= Instructions.I32_ROTR) || + opcode <= Instructions.I32_UNOP_BASE + Instructions.IUNOP_LAST) || + (opcode >= Instructions.I32_ADD && opcode <= Instructions.I32_ROTR) || (opcode >= Instructions.I64_RELOP_BASE && - opcode <= - Instructions.I64_RELOP_BASE + Instructions.IRELOP_LAST) || + opcode <= Instructions.I64_RELOP_BASE + Instructions.IRELOP_LAST) || (opcode >= Instructions.I64_UNOP_BASE && - opcode <= - Instructions.I64_UNOP_BASE + Instructions.IUNOP_LAST) || - (opcode >= Instructions.I64_ADD && - opcode <= Instructions.I64_ROTR) || + opcode <= Instructions.I64_UNOP_BASE + Instructions.IUNOP_LAST) || + (opcode >= Instructions.I64_ADD && opcode <= Instructions.I64_ROTR) || (opcode == Instructions.I32_WRAP_I64) || - (opcode == Instructions.I64_EXTEND_I32_S || - opcode == Instructions.I64_EXTEND_I32_U) || - (opcode >= Instructions.I32_EXTEND_8S && - opcode <= Instructions.I64_EXTEND_32S) || + (opcode == Instructions.I64_EXTEND_I32_S || opcode == Instructions.I64_EXTEND_I32_U) || + (opcode >= Instructions.I32_EXTEND_8S && opcode <= Instructions.I64_EXTEND_32S) || (opcode >= Instructions.I32_REINTERPRET_F32 && opcode <= Instructions.F64_REINTERPRET_I64) ) { @@ -128,26 +110,16 @@ contract OneStepProofEntry is IOneStepProofEntry { } else if ( (opcode >= Instructions.GET_GLOBAL_STATE_BYTES32 && opcode <= Instructions.SET_GLOBAL_STATE_U64) || - (opcode >= Instructions.READ_PRE_IMAGE && - opcode <= Instructions.HALT_AND_SET_FINISHED) + (opcode >= Instructions.READ_PRE_IMAGE && opcode <= Instructions.HALT_AND_SET_FINISHED) ) { prover = proverHostIo; } else { prover = prover0; } - (mach, mod) = prover.executeOneStep( - execCtx, - mach, - mod, - inst, - proof - ); + (mach, mod) = prover.executeOneStep(execCtx, mach, mod, inst, proof); - mach.modulesRoot = modProof.computeRootFromModule( - oldModIdx, - mod - ); + mach.modulesRoot = modProof.computeRootFromModule(oldModIdx, mod); return mach.hash(); } diff --git a/solgen/src/osp/OneStepProver0.sol b/solgen/src/osp/OneStepProver0.sol index 81c4da2744..0ed3641c12 100644 --- a/solgen/src/osp/OneStepProver0.sol +++ b/solgen/src/osp/OneStepProver0.sol @@ -9,397 +9,555 @@ import "./IOneStepProver.sol"; contract OneStepProver0 is IOneStepProver { using MerkleProofLib for MerkleProof; - using PcStackLib for PcStack; - using StackFrameLib for StackFrameWindow; - using ValueLib for Value; - using ValueStackLib for ValueStack; - - function executeUnreachable(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - mach.status = MachineStatus.ERRORED; - } - - function executeNop(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - // :) - } - - function executeConstPush(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint16 opcode = inst.opcode; - ValueType ty; - if (opcode == Instructions.I32_CONST) { - ty = ValueType.I32; - } else if (opcode == Instructions.I64_CONST) { - ty = ValueType.I64; - } else if (opcode == Instructions.F32_CONST) { - ty = ValueType.F32; - } else if (opcode == Instructions.F64_CONST) { - ty = ValueType.F64; - } else if (opcode == Instructions.PUSH_STACK_BOUNDARY) { - ty = ValueType.STACK_BOUNDARY; - } else { - revert("CONST_PUSH_INVALID_OPCODE"); - } - - mach.valueStack.push(Value({ - valueType: ty, - contents: uint64(inst.argumentData) - })); - } - - function executeDrop(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - mach.valueStack.pop(); - } - - function executeSelect(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - uint32 selector = mach.valueStack.pop().assumeI32(); - Value memory b = mach.valueStack.pop(); - Value memory a = mach.valueStack.pop(); - - if (selector != 0) { - mach.valueStack.push(a); - } else { - mach.valueStack.push(b); - } - } - - function executeBlock(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 targetPc = uint32(inst.argumentData); - require(targetPc == inst.argumentData, "BAD_BLOCK_PC"); - mach.blockStack.push(targetPc); - } - - function executeBranch(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - mach.functionPc = mach.blockStack.pop(); - } - - function executeBranchIf(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - uint32 cond = mach.valueStack.pop().assumeI32(); - if (cond != 0) { - // Jump to target - mach.functionPc = mach.blockStack.pop(); - } - } - - function executeReturn(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - StackFrame memory frame = mach.frameStack.pop(); - if (frame.returnPc.valueType == ValueType.REF_NULL) { - mach.status = MachineStatus.ERRORED; - return; - } else if (frame.returnPc.valueType != ValueType.INTERNAL_REF) { - revert("INVALID_RETURN_PC_TYPE"); - } - uint256 data = frame.returnPc.contents; - uint32 pc = uint32(data); - uint32 func = uint32(data >> 32); - uint32 mod = uint32(data >> 64); - require(data >> 96 == 0, "INVALID_RETURN_PC_DATA"); - mach.functionPc = pc; - mach.functionIdx = func; - mach.moduleIdx = mod; - } - - function createReturnValue(Machine memory mach) internal pure returns (Value memory) { - uint256 returnData = 0; - returnData |= mach.functionPc; - returnData |= uint256(mach.functionIdx) << 32; - returnData |= uint256(mach.moduleIdx) << 64; - return Value({ - valueType: ValueType.INTERNAL_REF, - contents: returnData - }); - } - - function executeCall(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - // Push the return pc to the stack - mach.valueStack.push(createReturnValue(mach)); - - // Push caller module info to the stack - StackFrame memory frame = mach.frameStack.peek(); - mach.valueStack.push(ValueLib.newI32(frame.callerModule)); - mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals)); - - // Jump to the target - uint32 idx = uint32(inst.argumentData); - require(idx == inst.argumentData, "BAD_CALL_DATA"); - mach.functionIdx = idx; - mach.functionPc = 0; - } - - function executeCrossModuleCall(Machine memory mach, Module memory mod, Instruction calldata inst, bytes calldata) internal pure { - // Push the return pc to the stack - mach.valueStack.push(createReturnValue(mach)); - - // Push caller module info to the stack - mach.valueStack.push(ValueLib.newI32(mach.moduleIdx)); - mach.valueStack.push(ValueLib.newI32(mod.internalsOffset)); - - // Jump to the target - uint32 func = uint32(inst.argumentData); - uint32 module = uint32(inst.argumentData >> 32); - require(inst.argumentData >> 64 == 0, "BAD_CROSS_MODULE_CALL_DATA"); - mach.moduleIdx = module; - mach.functionIdx = func; - mach.functionPc = 0; - } - - function executeCallerModuleInternalCall(Machine memory mach, Module memory mod, Instruction calldata inst, bytes calldata) internal pure { - // Push the return pc to the stack - mach.valueStack.push(createReturnValue(mach)); - - // Push caller module info to the stack - mach.valueStack.push(ValueLib.newI32(mach.moduleIdx)); - mach.valueStack.push(ValueLib.newI32(mod.internalsOffset)); - - StackFrame memory frame = mach.frameStack.peek(); - if (frame.callerModuleInternals == 0) { - // The caller module has no internals - mach.status = MachineStatus.ERRORED; - return; - } - - // Jump to the target - uint32 offset = uint32(inst.argumentData); - require(offset == inst.argumentData, "BAD_CALLER_INTERNAL_CALL_DATA"); - mach.moduleIdx = frame.callerModule; - mach.functionIdx = frame.callerModuleInternals + offset; - mach.functionPc = 0; - } - - function executeCallIndirect(Machine memory mach, Module memory mod, Instruction calldata inst, bytes calldata proof) internal pure { - uint32 funcIdx; - { - uint32 elementIdx = mach.valueStack.pop().assumeI32(); - - // Prove metadata about the instruction and tables - bytes32 elemsRoot; - bytes32 wantedFuncTypeHash; - uint256 offset = 0; - { - uint64 tableIdx; - uint8 tableType; - uint64 tableSize; - MerkleProof memory tableMerkleProof; - (tableIdx, offset) = Deserialize.u64(proof, offset); - (wantedFuncTypeHash, offset) = Deserialize.b32(proof, offset); - (tableType, offset) = Deserialize.u8(proof, offset); - (tableSize, offset) = Deserialize.u64(proof, offset); - (elemsRoot, offset) = Deserialize.b32(proof, offset); - (tableMerkleProof, offset) = Deserialize.merkleProof(proof, offset); - - // Validate the information by recomputing known hashes - bytes32 recomputed = keccak256(abi.encodePacked("Call indirect:", tableIdx, wantedFuncTypeHash)); - require(recomputed == bytes32(inst.argumentData), "BAD_CALL_INDIRECT_DATA"); - recomputed = tableMerkleProof.computeRootFromTable(tableIdx, tableType, tableSize, elemsRoot); - require(recomputed == mod.tablesMerkleRoot, "BAD_TABLES_ROOT"); - - // Check if the table access is out of bounds - if (elementIdx >= tableSize) { - mach.status = MachineStatus.ERRORED; - return; - } - } - - bytes32 elemFuncTypeHash; - Value memory functionPointer; - MerkleProof memory elementMerkleProof; - (elemFuncTypeHash, offset) = Deserialize.b32(proof, offset); - (functionPointer, offset) = Deserialize.value(proof, offset); - (elementMerkleProof, offset) = Deserialize.merkleProof(proof, offset); - bytes32 recomputedElemRoot = elementMerkleProof.computeRootFromElement(elementIdx, elemFuncTypeHash, functionPointer); - require(recomputedElemRoot == elemsRoot, "BAD_ELEMENTS_ROOT"); - - if (elemFuncTypeHash != wantedFuncTypeHash) { - mach.status = MachineStatus.ERRORED; - return; - } - - if (functionPointer.valueType == ValueType.REF_NULL) { - mach.status = MachineStatus.ERRORED; - return; - } else if (functionPointer.valueType == ValueType.FUNC_REF) { - funcIdx = uint32(functionPointer.contents); - require(funcIdx == functionPointer.contents, "BAD_FUNC_REF_CONTENTS"); - } else { - revert("BAD_ELEM_TYPE"); - } - } - - // Push the return pc to the stack - mach.valueStack.push(createReturnValue(mach)); - - // Push caller module info to the stack - StackFrame memory frame = mach.frameStack.peek(); - mach.valueStack.push(ValueLib.newI32(frame.callerModule)); - mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals)); - - // Jump to the target - mach.functionIdx = funcIdx; - mach.functionPc = 0; - } - - function executeArbitraryJumpIf(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 cond = mach.valueStack.pop().assumeI32(); - if (cond != 0) { - // Jump to target - uint32 pc = uint32(inst.argumentData); - require(pc == inst.argumentData, "BAD_CALL_DATA"); - mach.functionPc = pc; - } - } - - function merkleProveGetValue(bytes32 merkleRoot, uint256 index, bytes calldata proof) internal pure returns (Value memory) { - uint256 offset = 0; - Value memory proposedVal; - MerkleProof memory merkle; - (proposedVal, offset) = Deserialize.value(proof, offset); - (merkle, offset) = Deserialize.merkleProof(proof, offset); - bytes32 recomputedRoot = merkle.computeRootFromValue(index, proposedVal); - require(recomputedRoot == merkleRoot, "WRONG_MERKLE_ROOT"); - return proposedVal; - } - - function merkleProveSetValue(bytes32 merkleRoot, uint256 index, Value memory newVal, bytes calldata proof) internal pure returns (bytes32) { - Value memory oldVal; - uint256 offset = 0; - MerkleProof memory merkle; - (oldVal, offset) = Deserialize.value(proof, offset); - (merkle, offset) = Deserialize.merkleProof(proof, offset); - bytes32 recomputedRoot = merkle.computeRootFromValue(index, oldVal); - require(recomputedRoot == merkleRoot, "WRONG_MERKLE_ROOT"); - return merkle.computeRootFromValue(index, newVal); - } - - function executeLocalGet(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata proof) internal pure { - StackFrame memory frame = mach.frameStack.peek(); - Value memory val = merkleProveGetValue(frame.localsMerkleRoot, inst.argumentData, proof); - mach.valueStack.push(val); - } - - function executeLocalSet(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata proof) internal pure { - Value memory newVal = mach.valueStack.pop(); - StackFrame memory frame = mach.frameStack.peek(); - frame.localsMerkleRoot = merkleProveSetValue(frame.localsMerkleRoot, inst.argumentData, newVal, proof); - } - - function executeGlobalGet(Machine memory mach, Module memory mod, Instruction calldata inst, bytes calldata proof) internal pure { - Value memory val = merkleProveGetValue(mod.globalsMerkleRoot, inst.argumentData, proof); - mach.valueStack.push(val); - } - - function executeGlobalSet(Machine memory mach, Module memory mod, Instruction calldata inst, bytes calldata proof) internal pure { - Value memory newVal = mach.valueStack.pop(); - mod.globalsMerkleRoot = merkleProveSetValue(mod.globalsMerkleRoot, inst.argumentData, newVal, proof); - } - - function executeEndBlock(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - mach.blockStack.pop(); - } - - function executeEndBlockIf(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - uint32 cond = mach.valueStack.peek().assumeI32(); - if (cond != 0) { - mach.blockStack.pop(); - } - } - - function executeInitFrame(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - Value memory callerModuleInternals = mach.valueStack.pop(); - Value memory callerModule = mach.valueStack.pop(); - Value memory returnPc = mach.valueStack.pop(); - StackFrame memory newFrame = StackFrame({ - returnPc: returnPc, - localsMerkleRoot: bytes32(inst.argumentData), - callerModule: callerModule.assumeI32(), - callerModuleInternals: callerModuleInternals.assumeI32() - }); - mach.frameStack.push(newFrame); - } - - function executeMoveInternal(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - Value memory val; - if (inst.opcode == Instructions.MOVE_FROM_STACK_TO_INTERNAL) { - val = mach.valueStack.pop(); - mach.internalStack.push(val); - } else if (inst.opcode == Instructions.MOVE_FROM_INTERNAL_TO_STACK) { - val = mach.internalStack.pop(); - mach.valueStack.push(val); - } else { - revert("MOVE_INTERNAL_INVALID_OPCODE"); - } - } - - function executeIsStackBoundary(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - Value memory val = mach.valueStack.pop(); - uint32 newContents = 0; - if (val.valueType == ValueType.STACK_BOUNDARY) { - newContents = 1; - } - mach.valueStack.push(ValueLib.newI32(newContents)); - } - - function executeDup(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - Value memory val = mach.valueStack.peek(); - mach.valueStack.push(val); - } - - function executeOneStep(ExecutionContext calldata, Machine calldata startMach, Module calldata startMod, Instruction calldata inst, bytes calldata proof) override pure external returns (Machine memory mach, Module memory mod) { - mach = startMach; - mod = startMod; - - uint16 opcode = inst.opcode; - - function(Machine memory, Module memory, Instruction calldata, bytes calldata) internal pure impl; - if (opcode == Instructions.UNREACHABLE) { - impl = executeUnreachable; - } else if (opcode == Instructions.NOP) { - impl = executeNop; - } else if (opcode == Instructions.BLOCK) { - impl = executeBlock; - } else if (opcode == Instructions.BRANCH) { - impl = executeBranch; - } else if (opcode == Instructions.BRANCH_IF) { - impl = executeBranchIf; - } else if (opcode == Instructions.RETURN) { - impl = executeReturn; - } else if (opcode == Instructions.CALL) { - impl = executeCall; - } else if (opcode == Instructions.CROSS_MODULE_CALL) { - impl = executeCrossModuleCall; - } else if (opcode == Instructions.CALLER_MODULE_INTERNAL_CALL) { - impl = executeCallerModuleInternalCall; - } else if (opcode == Instructions.CALL_INDIRECT) { - impl = executeCallIndirect; - } else if (opcode == Instructions.END_BLOCK) { - impl = executeEndBlock; - } else if (opcode == Instructions.END_BLOCK_IF) { - impl = executeEndBlockIf; - } else if (opcode == Instructions.ARBITRARY_JUMP_IF) { - impl = executeArbitraryJumpIf; - } else if (opcode == Instructions.LOCAL_GET) { - impl = executeLocalGet; - } else if (opcode == Instructions.LOCAL_SET) { - impl = executeLocalSet; - } else if (opcode == Instructions.GLOBAL_GET) { - impl = executeGlobalGet; - } else if (opcode == Instructions.GLOBAL_SET) { - impl = executeGlobalSet; - } else if (opcode == Instructions.INIT_FRAME) { - impl = executeInitFrame; - } else if (opcode == Instructions.DROP) { - impl = executeDrop; - } else if (opcode == Instructions.SELECT) { - impl = executeSelect; - } else if (opcode >= Instructions.I32_CONST && opcode <= Instructions.F64_CONST || opcode == Instructions.PUSH_STACK_BOUNDARY) { - impl = executeConstPush; - } else if (opcode == Instructions.MOVE_FROM_STACK_TO_INTERNAL || opcode == Instructions.MOVE_FROM_INTERNAL_TO_STACK) { - impl = executeMoveInternal; - } else if (opcode == Instructions.IS_STACK_BOUNDARY) { - impl = executeIsStackBoundary; - } else if (opcode == Instructions.DUP) { - impl = executeDup; - } else { - revert("INVALID_OPCODE"); - } - - impl(mach, mod, inst, proof); - } + using PcStackLib for PcStack; + using StackFrameLib for StackFrameWindow; + using ValueLib for Value; + using ValueStackLib for ValueStack; + + function executeUnreachable( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + mach.status = MachineStatus.ERRORED; + } + + function executeNop( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + // :) + } + + function executeConstPush( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint16 opcode = inst.opcode; + ValueType ty; + if (opcode == Instructions.I32_CONST) { + ty = ValueType.I32; + } else if (opcode == Instructions.I64_CONST) { + ty = ValueType.I64; + } else if (opcode == Instructions.F32_CONST) { + ty = ValueType.F32; + } else if (opcode == Instructions.F64_CONST) { + ty = ValueType.F64; + } else if (opcode == Instructions.PUSH_STACK_BOUNDARY) { + ty = ValueType.STACK_BOUNDARY; + } else { + revert("CONST_PUSH_INVALID_OPCODE"); + } + + mach.valueStack.push(Value({valueType: ty, contents: uint64(inst.argumentData)})); + } + + function executeDrop( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + mach.valueStack.pop(); + } + + function executeSelect( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + uint32 selector = mach.valueStack.pop().assumeI32(); + Value memory b = mach.valueStack.pop(); + Value memory a = mach.valueStack.pop(); + + if (selector != 0) { + mach.valueStack.push(a); + } else { + mach.valueStack.push(b); + } + } + + function executeBlock( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 targetPc = uint32(inst.argumentData); + require(targetPc == inst.argumentData, "BAD_BLOCK_PC"); + mach.blockStack.push(targetPc); + } + + function executeBranch( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + mach.functionPc = mach.blockStack.pop(); + } + + function executeBranchIf( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + uint32 cond = mach.valueStack.pop().assumeI32(); + if (cond != 0) { + // Jump to target + mach.functionPc = mach.blockStack.pop(); + } + } + + function executeReturn( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + StackFrame memory frame = mach.frameStack.pop(); + if (frame.returnPc.valueType == ValueType.REF_NULL) { + mach.status = MachineStatus.ERRORED; + return; + } else if (frame.returnPc.valueType != ValueType.INTERNAL_REF) { + revert("INVALID_RETURN_PC_TYPE"); + } + uint256 data = frame.returnPc.contents; + uint32 pc = uint32(data); + uint32 func = uint32(data >> 32); + uint32 mod = uint32(data >> 64); + require(data >> 96 == 0, "INVALID_RETURN_PC_DATA"); + mach.functionPc = pc; + mach.functionIdx = func; + mach.moduleIdx = mod; + } + + function createReturnValue(Machine memory mach) internal pure returns (Value memory) { + uint256 returnData = 0; + returnData |= mach.functionPc; + returnData |= uint256(mach.functionIdx) << 32; + returnData |= uint256(mach.moduleIdx) << 64; + return Value({valueType: ValueType.INTERNAL_REF, contents: returnData}); + } + + function executeCall( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller module info to the stack + StackFrame memory frame = mach.frameStack.peek(); + mach.valueStack.push(ValueLib.newI32(frame.callerModule)); + mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals)); + + // Jump to the target + uint32 idx = uint32(inst.argumentData); + require(idx == inst.argumentData, "BAD_CALL_DATA"); + mach.functionIdx = idx; + mach.functionPc = 0; + } + + function executeCrossModuleCall( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata + ) internal pure { + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller module info to the stack + mach.valueStack.push(ValueLib.newI32(mach.moduleIdx)); + mach.valueStack.push(ValueLib.newI32(mod.internalsOffset)); + + // Jump to the target + uint32 func = uint32(inst.argumentData); + uint32 module = uint32(inst.argumentData >> 32); + require(inst.argumentData >> 64 == 0, "BAD_CROSS_MODULE_CALL_DATA"); + mach.moduleIdx = module; + mach.functionIdx = func; + mach.functionPc = 0; + } + + function executeCallerModuleInternalCall( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata + ) internal pure { + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller module info to the stack + mach.valueStack.push(ValueLib.newI32(mach.moduleIdx)); + mach.valueStack.push(ValueLib.newI32(mod.internalsOffset)); + + StackFrame memory frame = mach.frameStack.peek(); + if (frame.callerModuleInternals == 0) { + // The caller module has no internals + mach.status = MachineStatus.ERRORED; + return; + } + + // Jump to the target + uint32 offset = uint32(inst.argumentData); + require(offset == inst.argumentData, "BAD_CALLER_INTERNAL_CALL_DATA"); + mach.moduleIdx = frame.callerModule; + mach.functionIdx = frame.callerModuleInternals + offset; + mach.functionPc = 0; + } + + function executeCallIndirect( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + uint32 funcIdx; + { + uint32 elementIdx = mach.valueStack.pop().assumeI32(); + + // Prove metadata about the instruction and tables + bytes32 elemsRoot; + bytes32 wantedFuncTypeHash; + uint256 offset = 0; + { + uint64 tableIdx; + uint8 tableType; + uint64 tableSize; + MerkleProof memory tableMerkleProof; + (tableIdx, offset) = Deserialize.u64(proof, offset); + (wantedFuncTypeHash, offset) = Deserialize.b32(proof, offset); + (tableType, offset) = Deserialize.u8(proof, offset); + (tableSize, offset) = Deserialize.u64(proof, offset); + (elemsRoot, offset) = Deserialize.b32(proof, offset); + (tableMerkleProof, offset) = Deserialize.merkleProof(proof, offset); + + // Validate the information by recomputing known hashes + bytes32 recomputed = keccak256( + abi.encodePacked("Call indirect:", tableIdx, wantedFuncTypeHash) + ); + require(recomputed == bytes32(inst.argumentData), "BAD_CALL_INDIRECT_DATA"); + recomputed = tableMerkleProof.computeRootFromTable( + tableIdx, + tableType, + tableSize, + elemsRoot + ); + require(recomputed == mod.tablesMerkleRoot, "BAD_TABLES_ROOT"); + + // Check if the table access is out of bounds + if (elementIdx >= tableSize) { + mach.status = MachineStatus.ERRORED; + return; + } + } + + bytes32 elemFuncTypeHash; + Value memory functionPointer; + MerkleProof memory elementMerkleProof; + (elemFuncTypeHash, offset) = Deserialize.b32(proof, offset); + (functionPointer, offset) = Deserialize.value(proof, offset); + (elementMerkleProof, offset) = Deserialize.merkleProof(proof, offset); + bytes32 recomputedElemRoot = elementMerkleProof.computeRootFromElement( + elementIdx, + elemFuncTypeHash, + functionPointer + ); + require(recomputedElemRoot == elemsRoot, "BAD_ELEMENTS_ROOT"); + + if (elemFuncTypeHash != wantedFuncTypeHash) { + mach.status = MachineStatus.ERRORED; + return; + } + + if (functionPointer.valueType == ValueType.REF_NULL) { + mach.status = MachineStatus.ERRORED; + return; + } else if (functionPointer.valueType == ValueType.FUNC_REF) { + funcIdx = uint32(functionPointer.contents); + require(funcIdx == functionPointer.contents, "BAD_FUNC_REF_CONTENTS"); + } else { + revert("BAD_ELEM_TYPE"); + } + } + + // Push the return pc to the stack + mach.valueStack.push(createReturnValue(mach)); + + // Push caller module info to the stack + StackFrame memory frame = mach.frameStack.peek(); + mach.valueStack.push(ValueLib.newI32(frame.callerModule)); + mach.valueStack.push(ValueLib.newI32(frame.callerModuleInternals)); + + // Jump to the target + mach.functionIdx = funcIdx; + mach.functionPc = 0; + } + + function executeArbitraryJumpIf( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 cond = mach.valueStack.pop().assumeI32(); + if (cond != 0) { + // Jump to target + uint32 pc = uint32(inst.argumentData); + require(pc == inst.argumentData, "BAD_CALL_DATA"); + mach.functionPc = pc; + } + } + + function merkleProveGetValue( + bytes32 merkleRoot, + uint256 index, + bytes calldata proof + ) internal pure returns (Value memory) { + uint256 offset = 0; + Value memory proposedVal; + MerkleProof memory merkle; + (proposedVal, offset) = Deserialize.value(proof, offset); + (merkle, offset) = Deserialize.merkleProof(proof, offset); + bytes32 recomputedRoot = merkle.computeRootFromValue(index, proposedVal); + require(recomputedRoot == merkleRoot, "WRONG_MERKLE_ROOT"); + return proposedVal; + } + + function merkleProveSetValue( + bytes32 merkleRoot, + uint256 index, + Value memory newVal, + bytes calldata proof + ) internal pure returns (bytes32) { + Value memory oldVal; + uint256 offset = 0; + MerkleProof memory merkle; + (oldVal, offset) = Deserialize.value(proof, offset); + (merkle, offset) = Deserialize.merkleProof(proof, offset); + bytes32 recomputedRoot = merkle.computeRootFromValue(index, oldVal); + require(recomputedRoot == merkleRoot, "WRONG_MERKLE_ROOT"); + return merkle.computeRootFromValue(index, newVal); + } + + function executeLocalGet( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + StackFrame memory frame = mach.frameStack.peek(); + Value memory val = merkleProveGetValue(frame.localsMerkleRoot, inst.argumentData, proof); + mach.valueStack.push(val); + } + + function executeLocalSet( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + Value memory newVal = mach.valueStack.pop(); + StackFrame memory frame = mach.frameStack.peek(); + frame.localsMerkleRoot = merkleProveSetValue( + frame.localsMerkleRoot, + inst.argumentData, + newVal, + proof + ); + } + + function executeGlobalGet( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + Value memory val = merkleProveGetValue(mod.globalsMerkleRoot, inst.argumentData, proof); + mach.valueStack.push(val); + } + + function executeGlobalSet( + Machine memory mach, + Module memory mod, + Instruction calldata inst, + bytes calldata proof + ) internal pure { + Value memory newVal = mach.valueStack.pop(); + mod.globalsMerkleRoot = merkleProveSetValue( + mod.globalsMerkleRoot, + inst.argumentData, + newVal, + proof + ); + } + + function executeEndBlock( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + mach.blockStack.pop(); + } + + function executeEndBlockIf( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + uint32 cond = mach.valueStack.peek().assumeI32(); + if (cond != 0) { + mach.blockStack.pop(); + } + } + + function executeInitFrame( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + Value memory callerModuleInternals = mach.valueStack.pop(); + Value memory callerModule = mach.valueStack.pop(); + Value memory returnPc = mach.valueStack.pop(); + StackFrame memory newFrame = StackFrame({ + returnPc: returnPc, + localsMerkleRoot: bytes32(inst.argumentData), + callerModule: callerModule.assumeI32(), + callerModuleInternals: callerModuleInternals.assumeI32() + }); + mach.frameStack.push(newFrame); + } + + function executeMoveInternal( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + Value memory val; + if (inst.opcode == Instructions.MOVE_FROM_STACK_TO_INTERNAL) { + val = mach.valueStack.pop(); + mach.internalStack.push(val); + } else if (inst.opcode == Instructions.MOVE_FROM_INTERNAL_TO_STACK) { + val = mach.internalStack.pop(); + mach.valueStack.push(val); + } else { + revert("MOVE_INTERNAL_INVALID_OPCODE"); + } + } + + function executeIsStackBoundary( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + Value memory val = mach.valueStack.pop(); + uint32 newContents = 0; + if (val.valueType == ValueType.STACK_BOUNDARY) { + newContents = 1; + } + mach.valueStack.push(ValueLib.newI32(newContents)); + } + + function executeDup( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + Value memory val = mach.valueStack.peek(); + mach.valueStack.push(val); + } + + function executeOneStep( + ExecutionContext calldata, + Machine calldata startMach, + Module calldata startMod, + Instruction calldata inst, + bytes calldata proof + ) external pure override returns (Machine memory mach, Module memory mod) { + mach = startMach; + mod = startMod; + + uint16 opcode = inst.opcode; + + function(Machine memory, Module memory, Instruction calldata, bytes calldata) + internal + pure impl; + if (opcode == Instructions.UNREACHABLE) { + impl = executeUnreachable; + } else if (opcode == Instructions.NOP) { + impl = executeNop; + } else if (opcode == Instructions.BLOCK) { + impl = executeBlock; + } else if (opcode == Instructions.BRANCH) { + impl = executeBranch; + } else if (opcode == Instructions.BRANCH_IF) { + impl = executeBranchIf; + } else if (opcode == Instructions.RETURN) { + impl = executeReturn; + } else if (opcode == Instructions.CALL) { + impl = executeCall; + } else if (opcode == Instructions.CROSS_MODULE_CALL) { + impl = executeCrossModuleCall; + } else if (opcode == Instructions.CALLER_MODULE_INTERNAL_CALL) { + impl = executeCallerModuleInternalCall; + } else if (opcode == Instructions.CALL_INDIRECT) { + impl = executeCallIndirect; + } else if (opcode == Instructions.END_BLOCK) { + impl = executeEndBlock; + } else if (opcode == Instructions.END_BLOCK_IF) { + impl = executeEndBlockIf; + } else if (opcode == Instructions.ARBITRARY_JUMP_IF) { + impl = executeArbitraryJumpIf; + } else if (opcode == Instructions.LOCAL_GET) { + impl = executeLocalGet; + } else if (opcode == Instructions.LOCAL_SET) { + impl = executeLocalSet; + } else if (opcode == Instructions.GLOBAL_GET) { + impl = executeGlobalGet; + } else if (opcode == Instructions.GLOBAL_SET) { + impl = executeGlobalSet; + } else if (opcode == Instructions.INIT_FRAME) { + impl = executeInitFrame; + } else if (opcode == Instructions.DROP) { + impl = executeDrop; + } else if (opcode == Instructions.SELECT) { + impl = executeSelect; + } else if ( + (opcode >= Instructions.I32_CONST && opcode <= Instructions.F64_CONST) || + opcode == Instructions.PUSH_STACK_BOUNDARY + ) { + impl = executeConstPush; + } else if ( + opcode == Instructions.MOVE_FROM_STACK_TO_INTERNAL || + opcode == Instructions.MOVE_FROM_INTERNAL_TO_STACK + ) { + impl = executeMoveInternal; + } else if (opcode == Instructions.IS_STACK_BOUNDARY) { + impl = executeIsStackBoundary; + } else if (opcode == Instructions.DUP) { + impl = executeDup; + } else { + revert("INVALID_OPCODE"); + } + + impl(mach, mod, inst, proof); + } } diff --git a/solgen/src/osp/OneStepProverHostIo.sol b/solgen/src/osp/OneStepProverHostIo.sol index 3fce5c6ae3..b5e9697819 100644 --- a/solgen/src/osp/OneStepProverHostIo.sol +++ b/solgen/src/osp/OneStepProverHostIo.sol @@ -13,7 +13,7 @@ contract OneStepProverHostIo is IOneStepProver { using GlobalStateLib for GlobalState; using MerkleProofLib for MerkleProof; using ModuleMemoryLib for ModuleMemory; - using ValueLib for Value; + using ValueLib for Value; using ValueStackLib for ValueStack; uint256 constant LEAF_SIZE = 32; @@ -56,7 +56,11 @@ contract OneStepProverHostIo is IOneStepProver { uint256 proofOffset = 0; bytes32 startLeafContents; MerkleProof memory merkleProof; - (startLeafContents, proofOffset, merkleProof) = mod.moduleMemory.proveLeaf(leafIdx, proof, proofOffset); + (startLeafContents, proofOffset, merkleProof) = mod.moduleMemory.proveLeaf( + leafIdx, + proof, + proofOffset + ); if (inst.opcode == Instructions.GET_GLOBAL_STATE_BYTES32) { mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory( @@ -70,10 +74,7 @@ contract OneStepProverHostIo is IOneStepProver { } } - function executeGetU64(Machine memory mach, GlobalState memory state) - internal - pure - { + function executeGetU64(Machine memory mach, GlobalState memory state) internal pure { uint32 idx = mach.valueStack.pop().assumeI32(); if (idx >= GlobalStateLib.U64_VALS_NUM) { @@ -84,10 +85,7 @@ contract OneStepProverHostIo is IOneStepProver { mach.valueStack.push(ValueLib.newI64(state.u64_vals[idx])); } - function executeSetU64(Machine memory mach, GlobalState memory state) - internal - pure - { + function executeSetU64(Machine memory mach, GlobalState memory state) internal pure { uint64 val = mach.valueStack.pop().assumeI64(); uint32 idx = mach.valueStack.pop().assumeI32(); @@ -116,11 +114,11 @@ contract OneStepProverHostIo is IOneStepProver { uint256 proofOffset = 0; bytes32 leafContents; MerkleProof memory merkleProof; - (leafContents, proofOffset, merkleProof) = mod.moduleMemory.proveLeaf( - leafIdx, - proof, - proofOffset - ); + (leafContents, proofOffset, merkleProof) = mod.moduleMemory.proveLeaf( + leafIdx, + proof, + proofOffset + ); bytes memory extracted; uint8 proofType = uint8(proof[proofOffset]); @@ -140,26 +138,19 @@ contract OneStepProverHostIo is IOneStepProver { } for (uint256 i = 0; i < extracted.length; i++) { - leafContents = setLeafByte( - leafContents, - i, - uint8(extracted[i]) - ); + leafContents = setLeafByte(leafContents, i, uint8(extracted[i])); } - mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory( - leafIdx, - leafContents - ); + mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory(leafIdx, leafContents); mach.valueStack.push(ValueLib.newI32(uint32(extracted.length))); } - function validateSequencerInbox(ExecutionContext calldata execCtx, uint64 msgIndex, bytes calldata message) - internal - view - returns (bool) - { + function validateSequencerInbox( + ExecutionContext calldata execCtx, + uint64 msgIndex, + bytes calldata message + ) internal view returns (bool) { require(message.length >= 40, "BAD_SEQINBOX_PROOF"); uint64 afterDelayedMsg; @@ -174,18 +165,16 @@ contract OneStepProverHostIo is IOneStepProver { if (afterDelayedMsg > 0) { delayedAcc = execCtx.delayedBridge.inboxAccs(afterDelayedMsg - 1); } - bytes32 acc = keccak256( - abi.encodePacked(beforeAcc, messageHash, delayedAcc) - ); + bytes32 acc = keccak256(abi.encodePacked(beforeAcc, messageHash, delayedAcc)); require(acc == execCtx.sequencerInbox.inboxAccs(msgIndex), "BAD_SEQINBOX_MESSAGE"); return true; } - function validateDelayedInbox(ExecutionContext calldata execCtx, uint64 msgIndex, bytes calldata message) - internal - view - returns (bool) - { + function validateDelayedInbox( + ExecutionContext calldata execCtx, + uint64 msgIndex, + bytes calldata message + ) internal view returns (bool) { require(message.length >= 161, "BAD_DELAYED_PROOF"); bytes32 beforeAcc; @@ -200,12 +189,7 @@ contract OneStepProverHostIo is IOneStepProver { (sender, ) = Deserialize.u256(message, 1); bytes32 messageHash = keccak256( - abi.encodePacked( - kind, - uint160(sender), - message[33:161], - messageDataHash - ) + abi.encodePacked(kind, uint160(sender), message[33:161], messageDataHash) ); bytes32 acc = Messages.accumulateInboxMessage(beforeAcc, messageHash); @@ -223,7 +207,10 @@ contract OneStepProverHostIo is IOneStepProver { uint256 messageOffset = mach.valueStack.pop().assumeI32(); uint256 ptr = mach.valueStack.pop().assumeI32(); uint256 msgIndex = mach.valueStack.pop().assumeI64(); - if (inst.argumentData == Instructions.INBOX_INDEX_SEQUENCER && msgIndex >= execCtx.maxInboxMessagesRead) { + if ( + inst.argumentData == Instructions.INBOX_INDEX_SEQUENCER && + msgIndex >= execCtx.maxInboxMessagesRead + ) { mach.status = MachineStatus.TOO_FAR; return; } @@ -281,10 +268,7 @@ contract OneStepProverHostIo is IOneStepProver { ); } - mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory( - leafIdx, - leafContents - ); + mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory(leafIdx, leafContents); mach.valueStack.push(ValueLib.newI32(i)); } @@ -310,10 +294,7 @@ contract OneStepProverHostIo is IOneStepProver { GlobalState memory state; uint256 proofOffset = 0; (state, proofOffset) = Deserialize.globalState(proof, proofOffset); - require( - state.hash() == mach.globalStateHash, - "BAD_GLOBAL_STATE" - ); + require(state.hash() == mach.globalStateHash, "BAD_GLOBAL_STATE"); if ( opcode == Instructions.GET_GLOBAL_STATE_BYTES32 || diff --git a/solgen/src/osp/OneStepProverMath.sol b/solgen/src/osp/OneStepProverMath.sol index 6f893c3fbd..4d17beb9e4 100644 --- a/solgen/src/osp/OneStepProverMath.sol +++ b/solgen/src/osp/OneStepProverMath.sol @@ -8,411 +8,504 @@ import "../state/Deserialize.sol"; import "./IOneStepProver.sol"; contract OneStepProverMath is IOneStepProver { - using ValueLib for Value; + using ValueLib for Value; using ValueStackLib for ValueStack; - function executeEqz(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - Value memory v = mach.valueStack.pop(); - if (inst.opcode == Instructions.I32_EQZ) { - require(v.valueType == ValueType.I32, "NOT_I32"); - } else if (inst.opcode == Instructions.I64_EQZ) { - require(v.valueType == ValueType.I64, "NOT_I64"); - } else { - revert("BAD_EQZ"); - } - - uint32 output; - if (v.contents == 0) { - output = 1; - } else { - output = 0; - } - - mach.valueStack.push(ValueLib.newI32(output)); - } - - function signExtend(uint32 a) internal pure returns (uint64) { - if (a & (1<<31) != 0) { - return uint64(a) | uint64(0xffffffff00000000); - } - return uint64(a); - } - - function I64RelOp(uint64 a, uint64 b, uint16 relop) internal pure returns (bool) { - if (relop == Instructions.IRELOP_EQ) { - return (a == b); - } else if (relop == Instructions.IRELOP_NE) { - return (a != b); - } else if (relop == Instructions.IRELOP_LT_S) { - return (int64(a) < int64(b)); - } else if (relop == Instructions.IRELOP_LT_U) { - return (a < b); - } else if (relop == Instructions.IRELOP_GT_S) { - return (int64(a) > int64(b)); - } else if (relop == Instructions.IRELOP_GT_U) { - return (a > b); - } else if (relop == Instructions.IRELOP_LE_S) { - return (int64(a) <= int64(b)); - } else if (relop == Instructions.IRELOP_LE_U) { - return (a <= b); - } else if (relop == Instructions.IRELOP_GE_S) { - return (int64(a) >= int64(b)); - } else if (relop == Instructions.IRELOP_GE_U) { - return (a >= b); - } else { - revert ("BAD IRELOP"); - } - } - - function executeI32RelOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 b = mach.valueStack.pop().assumeI32(); - uint32 a = mach.valueStack.pop().assumeI32(); - - uint16 relop = inst.opcode - Instructions.I32_RELOP_BASE; - uint64 a64; - uint64 b64; - - if (relop == Instructions.IRELOP_LT_S || relop == Instructions.IRELOP_GT_S || - relop == Instructions.IRELOP_LE_S || relop == Instructions.IRELOP_GE_S) { - a64 = signExtend(a); - b64 = signExtend(b); - } else { - a64 = uint64(a); - b64 = uint64(b); - } - - bool res = I64RelOp(a64, b64, relop); - - mach.valueStack.push(ValueLib.newBoolean(res)); - } - - function executeI64RelOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint64 b = mach.valueStack.pop().assumeI64(); - uint64 a = mach.valueStack.pop().assumeI64(); - - uint16 relop = inst.opcode - Instructions.I64_RELOP_BASE; - - bool res = I64RelOp(a, b, relop); - - mach.valueStack.push(ValueLib.newBoolean(res)); - } - - - function genericIUnOp(uint64 a, uint16 unop, uint16 bits) internal pure returns (uint32) { - require(bits == 32 || bits == 64, "WRONG USE OF genericUnOp"); - if (unop == Instructions.IUNOP_CLZ) { - /* curbits is one-based to keep with unsigned mathematics */ - uint32 curbit = bits; - while (curbit > 0 && (a & (1 << (curbit - 1)) == 0)) { - curbit -= 1; - } - return (bits - curbit); - } else if (unop == Instructions.IUNOP_CTZ) { - uint32 curbit = 0; - while (curbit < bits && ((a & (1 << curbit)) == 0)) { - curbit += 1; - } - return curbit; - } else if (unop == Instructions.IUNOP_POPCNT) { - uint32 curbit = 0; - uint32 res = 0; - while (curbit < bits) { - if ((a & (1 << curbit)) != 0) { - res += 1; - } - curbit++; - } - return res; - } - revert("BAD IUnOp"); - } - - function executeI32UnOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 a = mach.valueStack.pop().assumeI32(); - - uint16 unop = inst.opcode - Instructions.I32_UNOP_BASE; - - uint32 res = genericIUnOp(a, unop, 32); - - mach.valueStack.push(ValueLib.newI32(res)); - } - - function executeI64UnOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint64 a = mach.valueStack.pop().assumeI64(); - - uint16 unop = inst.opcode - Instructions.I64_UNOP_BASE; - - uint64 res = uint64(genericIUnOp(a, unop, 64)); - - mach.valueStack.push(ValueLib.newI64(res)); - } - - function rotl32(uint32 a, uint32 b) internal pure returns (uint32) { - b %= 32; - return (a << b) | (a >> (32 - b)); - } - - function rotl64(uint64 a, uint64 b) internal pure returns (uint64) { - b %= 64; - return (a << b) | (a >> (64 - b)); - } - - function rotr32(uint32 a, uint32 b) internal pure returns (uint32) { - b %= 32; - return (a >> b) | (a << (32 - b)); - } - - function rotr64(uint64 a, uint64 b) internal pure returns (uint64) { - b %= 64; - return (a >> b) | (a << (64 - b)); - } - - function genericBinOp(uint64 a, uint64 b, uint16 opcodeOffset) internal pure returns (uint64) { - unchecked { - if (opcodeOffset == 0) { - // add - return a + b; - } else if (opcodeOffset == 1) { - // sub - return a - b; - } else if (opcodeOffset == 2) { - // mul - return a * b; - } else if (opcodeOffset == 4) { - // div_u - if (b == 0) { - return 0; - } - return a / b; - } else if (opcodeOffset == 6) { - // rem_u - if (b == 0) { - return 0; - } - return a % b; - } else if (opcodeOffset == 7) { - // and - return a & b; - } else if (opcodeOffset == 8) { - // or - return a | b; - } else if (opcodeOffset == 9) { - // xor - return a ^ b; - } else { - revert("INVALID_GENERIC_BIN_OP"); - } - } - } - - function executeI32BinOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 b = mach.valueStack.pop().assumeI32(); - uint32 a = mach.valueStack.pop().assumeI32(); - uint32 res; - - uint16 opcodeOffset = inst.opcode - Instructions.I32_ADD; - - unchecked { - if (opcodeOffset == 3) { - // div_s - if (b == 0) { - res = 0; - } else { - res = uint32(int32(a) / int32(b)); - } - } else if (opcodeOffset == 5) { - // rem_s - if (b == 0) { - res = 0; - } else { - res = uint32(int32(a) % int32(b)); - } - } else if (opcodeOffset == 10) { - // shl - res = a << (b % 32); - } else if (opcodeOffset == 12) { - // shr_u - res = a >> (b % 32); - } else if (opcodeOffset == 11) { - // shr_s - res = uint32(int32(a) >> b); - } else if (opcodeOffset == 13) { - // rotl - res = rotl32(a, b); - } else if (opcodeOffset == 14) { - // rotr - res = rotr32(a, b); - } else { - res = uint32(genericBinOp(a, b, opcodeOffset)); - } - } - - mach.valueStack.push(ValueLib.newI32(res)); - } - - function executeI64BinOp(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint64 b = mach.valueStack.pop().assumeI64(); - uint64 a = mach.valueStack.pop().assumeI64(); - uint64 res; - - uint16 opcodeOffset = inst.opcode - Instructions.I64_ADD; - - unchecked { - if (opcodeOffset == 3) { - // div_s - if (b == 0) { - res = 0; - } else { - res = uint64(int64(a) / int64(b)); - } - } else if (opcodeOffset == 5) { - // rem_s - if (b == 0) { - res = 0; - } else { - res = uint64(int64(a) % int64(b)); - } - } else if (opcodeOffset == 10) { - // shl - res = a << (b % 64); - } else if (opcodeOffset == 12) { - // shr_u - res = a >> (b % 64); - } else if (opcodeOffset == 11) { - // shr_s - res = uint64(int64(a) >> b); - } else if (opcodeOffset == 13) { - // rotl - res = rotl64(a, b); - } else if (opcodeOffset == 14) { - // rotr - res = rotr64(a, b); - } else { - res = genericBinOp(a, b, opcodeOffset); - } - } - - mach.valueStack.push(ValueLib.newI64(res)); - } - - function executeI32WrapI64(Machine memory mach, Module memory, Instruction calldata, bytes calldata) internal pure { - uint64 a = mach.valueStack.pop().assumeI64(); - - uint32 a32 = uint32(a); - - mach.valueStack.push(ValueLib.newI32(a32)); - } - - function executeI64ExtendI32(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - uint32 a = mach.valueStack.pop().assumeI32(); - - uint64 a64; - - if (inst.opcode == Instructions.I64_EXTEND_I32_S) { - a64 = signExtend(a); - } else { - a64 = uint64(a); - } - - mach.valueStack.push(ValueLib.newI64(a64)); - } - - function executeExtendSameType(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - ValueType ty; - uint8 sourceBits; - if (inst.opcode == Instructions.I32_EXTEND_8S) { - ty = ValueType.I32; - sourceBits = 8; - } else if (inst.opcode == Instructions.I32_EXTEND_16S) { - ty = ValueType.I32; - sourceBits = 16; - } else if (inst.opcode == Instructions.I64_EXTEND_8S) { - ty = ValueType.I64; - sourceBits = 8; - } else if (inst.opcode == Instructions.I64_EXTEND_16S) { - ty = ValueType.I64; - sourceBits = 16; - } else if (inst.opcode == Instructions.I64_EXTEND_32S) { - ty = ValueType.I64; - sourceBits = 32; - } else { - revert("INVALID_EXTEND_SAME_TYPE"); - } - uint256 resultMask; - if (ty == ValueType.I32) { - resultMask = (1 << 32) - 1; - } else { - resultMask = (1 << 64) - 1; - } - Value memory val = mach.valueStack.pop(); - require(val.valueType == ty, "BAD_EXTEND_SAME_TYPE_TYPE"); - uint256 sourceMask = (1 << sourceBits) - 1; - val.contents &= sourceMask; - if (val.contents & (1 << (sourceBits - 1)) != 0) { - // Extend sign flag - val.contents |= resultMask & ~sourceMask; - } - mach.valueStack.push(val); - } - - function executeReinterpret(Machine memory mach, Module memory, Instruction calldata inst, bytes calldata) internal pure { - ValueType destTy; - ValueType sourceTy; - if (inst.opcode == Instructions.I32_REINTERPRET_F32) { - destTy = ValueType.I32; - sourceTy = ValueType.F32; - } else if (inst.opcode == Instructions.I64_REINTERPRET_F64) { - destTy = ValueType.I64; - sourceTy = ValueType.F64; - } else if (inst.opcode == Instructions.F32_REINTERPRET_I32) { - destTy = ValueType.F32; - sourceTy = ValueType.I32; - } else if (inst.opcode == Instructions.F64_REINTERPRET_I64) { - destTy = ValueType.F64; - sourceTy = ValueType.I64; - } else { - revert("INVALID_REINTERPRET"); - } - Value memory val = mach.valueStack.pop(); - require(val.valueType == sourceTy, "INVALID_REINTERPRET_TYPE"); - val.valueType = destTy; - mach.valueStack.push(val); - } - - function executeOneStep(ExecutionContext calldata, Machine calldata startMach, Module calldata startMod, Instruction calldata inst, bytes calldata proof) override pure external returns (Machine memory mach, Module memory mod) { - mach = startMach; - mod = startMod; - - uint16 opcode = inst.opcode; - - function(Machine memory, Module memory, Instruction calldata, bytes calldata) internal pure impl; - if (opcode == Instructions.I32_EQZ || opcode == Instructions.I64_EQZ) { - impl = executeEqz; - } else if (opcode >= Instructions.I32_RELOP_BASE && opcode <= Instructions.I32_RELOP_BASE + Instructions.IRELOP_LAST) { - impl = executeI32RelOp; - } else if (opcode >= Instructions.I32_UNOP_BASE && opcode <= Instructions.I32_UNOP_BASE + Instructions.IUNOP_LAST) { - impl = executeI32UnOp; - } else if (opcode >= Instructions.I32_ADD && opcode <= Instructions.I32_ROTR) { - impl = executeI32BinOp; - } else if (opcode >= Instructions.I64_RELOP_BASE && opcode <= Instructions.I64_RELOP_BASE + Instructions.IRELOP_LAST) { - impl = executeI64RelOp; - } else if (opcode >= Instructions.I64_UNOP_BASE && opcode <= Instructions.I64_UNOP_BASE + Instructions.IUNOP_LAST) { - impl = executeI64UnOp; - } else if (opcode >= Instructions.I64_ADD && opcode <= Instructions.I64_ROTR) { - impl = executeI64BinOp; - } else if (opcode == Instructions.I32_WRAP_I64) { - impl = executeI32WrapI64; - } else if (opcode == Instructions.I64_EXTEND_I32_S || opcode == Instructions.I64_EXTEND_I32_U) { - impl = executeI64ExtendI32; - } else if (opcode >= Instructions.I32_EXTEND_8S && opcode <= Instructions.I64_EXTEND_32S) { - impl = executeExtendSameType; - } else if (opcode >= Instructions.I32_REINTERPRET_F32 && opcode <= Instructions.F64_REINTERPRET_I64) { - impl = executeReinterpret; - } else { - revert("INVALID_OPCODE"); - } - - impl(mach, mod, inst, proof); - } + function executeEqz( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + Value memory v = mach.valueStack.pop(); + if (inst.opcode == Instructions.I32_EQZ) { + require(v.valueType == ValueType.I32, "NOT_I32"); + } else if (inst.opcode == Instructions.I64_EQZ) { + require(v.valueType == ValueType.I64, "NOT_I64"); + } else { + revert("BAD_EQZ"); + } + + uint32 output; + if (v.contents == 0) { + output = 1; + } else { + output = 0; + } + + mach.valueStack.push(ValueLib.newI32(output)); + } + + function signExtend(uint32 a) internal pure returns (uint64) { + if (a & (1 << 31) != 0) { + return uint64(a) | uint64(0xffffffff00000000); + } + return uint64(a); + } + + function I64RelOp( + uint64 a, + uint64 b, + uint16 relop + ) internal pure returns (bool) { + if (relop == Instructions.IRELOP_EQ) { + return (a == b); + } else if (relop == Instructions.IRELOP_NE) { + return (a != b); + } else if (relop == Instructions.IRELOP_LT_S) { + return (int64(a) < int64(b)); + } else if (relop == Instructions.IRELOP_LT_U) { + return (a < b); + } else if (relop == Instructions.IRELOP_GT_S) { + return (int64(a) > int64(b)); + } else if (relop == Instructions.IRELOP_GT_U) { + return (a > b); + } else if (relop == Instructions.IRELOP_LE_S) { + return (int64(a) <= int64(b)); + } else if (relop == Instructions.IRELOP_LE_U) { + return (a <= b); + } else if (relop == Instructions.IRELOP_GE_S) { + return (int64(a) >= int64(b)); + } else if (relop == Instructions.IRELOP_GE_U) { + return (a >= b); + } else { + revert("BAD IRELOP"); + } + } + + function executeI32RelOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 b = mach.valueStack.pop().assumeI32(); + uint32 a = mach.valueStack.pop().assumeI32(); + + uint16 relop = inst.opcode - Instructions.I32_RELOP_BASE; + uint64 a64; + uint64 b64; + + if ( + relop == Instructions.IRELOP_LT_S || + relop == Instructions.IRELOP_GT_S || + relop == Instructions.IRELOP_LE_S || + relop == Instructions.IRELOP_GE_S + ) { + a64 = signExtend(a); + b64 = signExtend(b); + } else { + a64 = uint64(a); + b64 = uint64(b); + } + + bool res = I64RelOp(a64, b64, relop); + + mach.valueStack.push(ValueLib.newBoolean(res)); + } + + function executeI64RelOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint64 b = mach.valueStack.pop().assumeI64(); + uint64 a = mach.valueStack.pop().assumeI64(); + + uint16 relop = inst.opcode - Instructions.I64_RELOP_BASE; + + bool res = I64RelOp(a, b, relop); + + mach.valueStack.push(ValueLib.newBoolean(res)); + } + + function genericIUnOp( + uint64 a, + uint16 unop, + uint16 bits + ) internal pure returns (uint32) { + require(bits == 32 || bits == 64, "WRONG USE OF genericUnOp"); + if (unop == Instructions.IUNOP_CLZ) { + /* curbits is one-based to keep with unsigned mathematics */ + uint32 curbit = bits; + while (curbit > 0 && (a & (1 << (curbit - 1)) == 0)) { + curbit -= 1; + } + return (bits - curbit); + } else if (unop == Instructions.IUNOP_CTZ) { + uint32 curbit = 0; + while (curbit < bits && ((a & (1 << curbit)) == 0)) { + curbit += 1; + } + return curbit; + } else if (unop == Instructions.IUNOP_POPCNT) { + uint32 curbit = 0; + uint32 res = 0; + while (curbit < bits) { + if ((a & (1 << curbit)) != 0) { + res += 1; + } + curbit++; + } + return res; + } + revert("BAD IUnOp"); + } + + function executeI32UnOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 a = mach.valueStack.pop().assumeI32(); + + uint16 unop = inst.opcode - Instructions.I32_UNOP_BASE; + + uint32 res = genericIUnOp(a, unop, 32); + + mach.valueStack.push(ValueLib.newI32(res)); + } + + function executeI64UnOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint64 a = mach.valueStack.pop().assumeI64(); + + uint16 unop = inst.opcode - Instructions.I64_UNOP_BASE; + + uint64 res = uint64(genericIUnOp(a, unop, 64)); + + mach.valueStack.push(ValueLib.newI64(res)); + } + + function rotl32(uint32 a, uint32 b) internal pure returns (uint32) { + b %= 32; + return (a << b) | (a >> (32 - b)); + } + + function rotl64(uint64 a, uint64 b) internal pure returns (uint64) { + b %= 64; + return (a << b) | (a >> (64 - b)); + } + + function rotr32(uint32 a, uint32 b) internal pure returns (uint32) { + b %= 32; + return (a >> b) | (a << (32 - b)); + } + + function rotr64(uint64 a, uint64 b) internal pure returns (uint64) { + b %= 64; + return (a >> b) | (a << (64 - b)); + } + + function genericBinOp( + uint64 a, + uint64 b, + uint16 opcodeOffset + ) internal pure returns (uint64) { + unchecked { + if (opcodeOffset == 0) { + // add + return a + b; + } else if (opcodeOffset == 1) { + // sub + return a - b; + } else if (opcodeOffset == 2) { + // mul + return a * b; + } else if (opcodeOffset == 4) { + // div_u + if (b == 0) { + return 0; + } + return a / b; + } else if (opcodeOffset == 6) { + // rem_u + if (b == 0) { + return 0; + } + return a % b; + } else if (opcodeOffset == 7) { + // and + return a & b; + } else if (opcodeOffset == 8) { + // or + return a | b; + } else if (opcodeOffset == 9) { + // xor + return a ^ b; + } else { + revert("INVALID_GENERIC_BIN_OP"); + } + } + } + + function executeI32BinOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 b = mach.valueStack.pop().assumeI32(); + uint32 a = mach.valueStack.pop().assumeI32(); + uint32 res; + + uint16 opcodeOffset = inst.opcode - Instructions.I32_ADD; + + unchecked { + if (opcodeOffset == 3) { + // div_s + if (b == 0) { + res = 0; + } else { + res = uint32(int32(a) / int32(b)); + } + } else if (opcodeOffset == 5) { + // rem_s + if (b == 0) { + res = 0; + } else { + res = uint32(int32(a) % int32(b)); + } + } else if (opcodeOffset == 10) { + // shl + res = a << (b % 32); + } else if (opcodeOffset == 12) { + // shr_u + res = a >> (b % 32); + } else if (opcodeOffset == 11) { + // shr_s + res = uint32(int32(a) >> b); + } else if (opcodeOffset == 13) { + // rotl + res = rotl32(a, b); + } else if (opcodeOffset == 14) { + // rotr + res = rotr32(a, b); + } else { + res = uint32(genericBinOp(a, b, opcodeOffset)); + } + } + + mach.valueStack.push(ValueLib.newI32(res)); + } + + function executeI64BinOp( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint64 b = mach.valueStack.pop().assumeI64(); + uint64 a = mach.valueStack.pop().assumeI64(); + uint64 res; + + uint16 opcodeOffset = inst.opcode - Instructions.I64_ADD; + + unchecked { + if (opcodeOffset == 3) { + // div_s + if (b == 0) { + res = 0; + } else { + res = uint64(int64(a) / int64(b)); + } + } else if (opcodeOffset == 5) { + // rem_s + if (b == 0) { + res = 0; + } else { + res = uint64(int64(a) % int64(b)); + } + } else if (opcodeOffset == 10) { + // shl + res = a << (b % 64); + } else if (opcodeOffset == 12) { + // shr_u + res = a >> (b % 64); + } else if (opcodeOffset == 11) { + // shr_s + res = uint64(int64(a) >> b); + } else if (opcodeOffset == 13) { + // rotl + res = rotl64(a, b); + } else if (opcodeOffset == 14) { + // rotr + res = rotr64(a, b); + } else { + res = genericBinOp(a, b, opcodeOffset); + } + } + + mach.valueStack.push(ValueLib.newI64(res)); + } + + function executeI32WrapI64( + Machine memory mach, + Module memory, + Instruction calldata, + bytes calldata + ) internal pure { + uint64 a = mach.valueStack.pop().assumeI64(); + + uint32 a32 = uint32(a); + + mach.valueStack.push(ValueLib.newI32(a32)); + } + + function executeI64ExtendI32( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + uint32 a = mach.valueStack.pop().assumeI32(); + + uint64 a64; + + if (inst.opcode == Instructions.I64_EXTEND_I32_S) { + a64 = signExtend(a); + } else { + a64 = uint64(a); + } + + mach.valueStack.push(ValueLib.newI64(a64)); + } + + function executeExtendSameType( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + ValueType ty; + uint8 sourceBits; + if (inst.opcode == Instructions.I32_EXTEND_8S) { + ty = ValueType.I32; + sourceBits = 8; + } else if (inst.opcode == Instructions.I32_EXTEND_16S) { + ty = ValueType.I32; + sourceBits = 16; + } else if (inst.opcode == Instructions.I64_EXTEND_8S) { + ty = ValueType.I64; + sourceBits = 8; + } else if (inst.opcode == Instructions.I64_EXTEND_16S) { + ty = ValueType.I64; + sourceBits = 16; + } else if (inst.opcode == Instructions.I64_EXTEND_32S) { + ty = ValueType.I64; + sourceBits = 32; + } else { + revert("INVALID_EXTEND_SAME_TYPE"); + } + uint256 resultMask; + if (ty == ValueType.I32) { + resultMask = (1 << 32) - 1; + } else { + resultMask = (1 << 64) - 1; + } + Value memory val = mach.valueStack.pop(); + require(val.valueType == ty, "BAD_EXTEND_SAME_TYPE_TYPE"); + uint256 sourceMask = (1 << sourceBits) - 1; + val.contents &= sourceMask; + if (val.contents & (1 << (sourceBits - 1)) != 0) { + // Extend sign flag + val.contents |= resultMask & ~sourceMask; + } + mach.valueStack.push(val); + } + + function executeReinterpret( + Machine memory mach, + Module memory, + Instruction calldata inst, + bytes calldata + ) internal pure { + ValueType destTy; + ValueType sourceTy; + if (inst.opcode == Instructions.I32_REINTERPRET_F32) { + destTy = ValueType.I32; + sourceTy = ValueType.F32; + } else if (inst.opcode == Instructions.I64_REINTERPRET_F64) { + destTy = ValueType.I64; + sourceTy = ValueType.F64; + } else if (inst.opcode == Instructions.F32_REINTERPRET_I32) { + destTy = ValueType.F32; + sourceTy = ValueType.I32; + } else if (inst.opcode == Instructions.F64_REINTERPRET_I64) { + destTy = ValueType.F64; + sourceTy = ValueType.I64; + } else { + revert("INVALID_REINTERPRET"); + } + Value memory val = mach.valueStack.pop(); + require(val.valueType == sourceTy, "INVALID_REINTERPRET_TYPE"); + val.valueType = destTy; + mach.valueStack.push(val); + } + + function executeOneStep( + ExecutionContext calldata, + Machine calldata startMach, + Module calldata startMod, + Instruction calldata inst, + bytes calldata proof + ) external pure override returns (Machine memory mach, Module memory mod) { + mach = startMach; + mod = startMod; + + uint16 opcode = inst.opcode; + + function(Machine memory, Module memory, Instruction calldata, bytes calldata) + internal + pure impl; + if (opcode == Instructions.I32_EQZ || opcode == Instructions.I64_EQZ) { + impl = executeEqz; + } else if ( + opcode >= Instructions.I32_RELOP_BASE && + opcode <= Instructions.I32_RELOP_BASE + Instructions.IRELOP_LAST + ) { + impl = executeI32RelOp; + } else if ( + opcode >= Instructions.I32_UNOP_BASE && + opcode <= Instructions.I32_UNOP_BASE + Instructions.IUNOP_LAST + ) { + impl = executeI32UnOp; + } else if (opcode >= Instructions.I32_ADD && opcode <= Instructions.I32_ROTR) { + impl = executeI32BinOp; + } else if ( + opcode >= Instructions.I64_RELOP_BASE && + opcode <= Instructions.I64_RELOP_BASE + Instructions.IRELOP_LAST + ) { + impl = executeI64RelOp; + } else if ( + opcode >= Instructions.I64_UNOP_BASE && + opcode <= Instructions.I64_UNOP_BASE + Instructions.IUNOP_LAST + ) { + impl = executeI64UnOp; + } else if (opcode >= Instructions.I64_ADD && opcode <= Instructions.I64_ROTR) { + impl = executeI64BinOp; + } else if (opcode == Instructions.I32_WRAP_I64) { + impl = executeI32WrapI64; + } else if ( + opcode == Instructions.I64_EXTEND_I32_S || opcode == Instructions.I64_EXTEND_I32_U + ) { + impl = executeI64ExtendI32; + } else if (opcode >= Instructions.I32_EXTEND_8S && opcode <= Instructions.I64_EXTEND_32S) { + impl = executeExtendSameType; + } else if ( + opcode >= Instructions.I32_REINTERPRET_F32 && opcode <= Instructions.F64_REINTERPRET_I64 + ) { + impl = executeReinterpret; + } else { + revert("INVALID_OPCODE"); + } + + impl(mach, mod, inst, proof); + } } - diff --git a/solgen/src/osp/OneStepProverMemory.sol b/solgen/src/osp/OneStepProverMemory.sol index cd1a455617..cd17c4f54d 100644 --- a/solgen/src/osp/OneStepProverMemory.sol +++ b/solgen/src/osp/OneStepProverMemory.sol @@ -9,17 +9,13 @@ import "./IOneStepProver.sol"; contract OneStepProverMemory is IOneStepProver { using MerkleProofLib for MerkleProof; using ModuleMemoryLib for ModuleMemory; - using ValueLib for Value; + using ValueLib for Value; using ValueStackLib for ValueStack; uint256 constant LEAF_SIZE = 32; uint64 constant PAGE_SIZE = 65536; - function pullLeafByte(bytes32 leaf, uint256 idx) - internal - pure - returns (uint8) - { + function pullLeafByte(bytes32 leaf, uint256 idx) internal pure returns (uint8) { require(idx < LEAF_SIZE, "BAD_PULL_LEAF_BYTE_IDX"); // Take into account that we are casting the leaf to a big-endian integer uint256 leafShift = (LEAF_SIZE - 1 - idx) * 8; @@ -110,8 +106,7 @@ contract OneStepProverMemory is IOneStepProver { } // Neither of these can overflow as they're computed with much less than 256 bit integers. - uint256 startIdx = inst.argumentData + - mach.valueStack.pop().assumeI32(); + uint256 startIdx = inst.argumentData + mach.valueStack.pop().assumeI32(); if (startIdx + readBytes > mod.moduleMemory.size) { mach.status = MachineStatus.ERRORED; return; @@ -126,8 +121,12 @@ contract OneStepProverMemory is IOneStepProver { uint256 leafIdx = idx / LEAF_SIZE; if (leafIdx != lastProvedLeafIdx) { // This hits the stack size if we phrase it as mod.moduleMemory.proveLeaf(...) - (lastProvedLeafContents, proofOffset, ) = ModuleMemoryLib - .proveLeaf(mod.moduleMemory, leafIdx, proof, proofOffset); + (lastProvedLeafContents, proofOffset, ) = ModuleMemoryLib.proveLeaf( + mod.moduleMemory, + leafIdx, + proof, + proofOffset + ); lastProvedLeafIdx = leafIdx; } uint256 indexWithinLeaf = idx % LEAF_SIZE; @@ -153,9 +152,7 @@ contract OneStepProverMemory is IOneStepProver { } } - mach.valueStack.push( - Value({valueType: ty, contents: readValue}) - ); + mach.valueStack.push(Value({valueType: ty, contents: readValue})); } function executeMemoryStore( @@ -208,8 +205,7 @@ contract OneStepProverMemory is IOneStepProver { } // Neither of these can overflow as they're computed with much less than 256 bit integers. - uint256 startIdx = inst.argumentData + - mach.valueStack.pop().assumeI32(); + uint256 startIdx = inst.argumentData + mach.valueStack.pop().assumeI32(); if (startIdx + writeBytes > mod.moduleMemory.size) { mach.status = MachineStatus.ERRORED; return; @@ -225,18 +221,13 @@ contract OneStepProverMemory is IOneStepProver { if (leafIdx != lastProvedLeafIdx) { if (lastProvedLeafIdx != ~uint256(0)) { // Apply the last leaf update - mod.moduleMemory.merkleRoot = lastProvedMerkle - .computeRootFromMemory( - lastProvedLeafIdx, - lastProvedLeafContents - ); + mod.moduleMemory.merkleRoot = lastProvedMerkle.computeRootFromMemory( + lastProvedLeafIdx, + lastProvedLeafContents + ); } // This hits the stack size if we phrase it as mod.moduleMemory.proveLeaf(...) - ( - lastProvedLeafContents, - proofOffset, - lastProvedMerkle - ) = ModuleMemoryLib.proveLeaf( + (lastProvedLeafContents, proofOffset, lastProvedMerkle) = ModuleMemoryLib.proveLeaf( mod.moduleMemory, leafIdx, proof, @@ -277,8 +268,7 @@ contract OneStepProverMemory is IOneStepProver { uint32 oldPages = uint32(mod.moduleMemory.size / PAGE_SIZE); uint32 growingPages = mach.valueStack.pop().assumeI32(); // Safe as the input integers are too small to overflow a uint256 - uint256 newSize = (uint256(oldPages) + uint256(growingPages)) * - PAGE_SIZE; + uint256 newSize = (uint256(oldPages) + uint256(growingPages)) * PAGE_SIZE; // Note: we require the size remain *below* 2^32, meaning the actual limit is 2^32-PAGE_SIZE if (newSize < (1 << 32)) { mod.moduleMemory.size = uint64(newSize); @@ -300,21 +290,12 @@ contract OneStepProverMemory is IOneStepProver { uint16 opcode = inst.opcode; - function( - Machine memory, - Module memory, - Instruction calldata, - bytes calldata - ) internal pure impl; - if ( - opcode >= Instructions.I32_LOAD && - opcode <= Instructions.I64_LOAD32_U - ) { + function(Machine memory, Module memory, Instruction calldata, bytes calldata) + internal + pure impl; + if (opcode >= Instructions.I32_LOAD && opcode <= Instructions.I64_LOAD32_U) { impl = executeMemoryLoad; - } else if ( - opcode >= Instructions.I32_STORE && - opcode <= Instructions.I64_STORE32 - ) { + } else if (opcode >= Instructions.I32_STORE && opcode <= Instructions.I64_STORE32) { impl = executeMemoryStore; } else if (opcode == Instructions.MEMORY_SIZE) { impl = executeMemorySize; diff --git a/solgen/src/precompiles/ArbAddressTable.sol b/solgen/src/precompiles/ArbAddressTable.sol index 468d38b3ea..4d4b908076 100644 --- a/solgen/src/precompiles/ArbAddressTable.sol +++ b/solgen/src/precompiles/ArbAddressTable.sol @@ -18,54 +18,57 @@ pragma solidity >=0.4.21 <0.9.0; -/** -* @title Allows registering / retrieving addresses at uint indices, saving calldata. -* @notice Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000066. -*/ +/** + * @title Allows registering / retrieving addresses at uint indices, saving calldata. + * @notice Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000066. + */ interface ArbAddressTable { /** - * @notice Check whether an address exists in the address table - * @param addr address to check for presence in table - * @return true if address is in table - */ - function addressExists(address addr) external view returns(bool); + * @notice Check whether an address exists in the address table + * @param addr address to check for presence in table + * @return true if address is in table + */ + function addressExists(address addr) external view returns (bool); /** - * @notice compress an address and return the result - * @param addr address to compress - * @return compressed address bytes - */ - function compress(address addr) external returns(bytes memory); + * @notice compress an address and return the result + * @param addr address to compress + * @return compressed address bytes + */ + function compress(address addr) external returns (bytes memory); /** - * @notice read a compressed address from a bytes buffer - * @param buf bytes buffer containing an address - * @param offset offset of target address - * @return resulting address and updated offset into the buffer (revert if buffer is too short) - */ - function decompress(bytes calldata buf, uint offset) external view returns(address, uint); + * @notice read a compressed address from a bytes buffer + * @param buf bytes buffer containing an address + * @param offset offset of target address + * @return resulting address and updated offset into the buffer (revert if buffer is too short) + */ + function decompress(bytes calldata buf, uint256 offset) + external + view + returns (address, uint256); /** - * @param addr address to lookup - * @return index of an address in the address table (revert if address isn't in the table) - */ - function lookup(address addr) external view returns(uint); + * @param addr address to lookup + * @return index of an address in the address table (revert if address isn't in the table) + */ + function lookup(address addr) external view returns (uint256); /** - * @param index index to lookup address - * @return address at a given index in address table (revert if index is beyond end of table) - */ - function lookupIndex(uint index) external view returns(address); + * @param index index to lookup address + * @return address at a given index in address table (revert if index is beyond end of table) + */ + function lookupIndex(uint256 index) external view returns (address); /** - * @notice Register an address in the address table - * @param addr address to register - * @return index of the address (existing index, or newly created index if not already registered) - */ - function register(address addr) external returns(uint); + * @notice Register an address in the address table + * @param addr address to register + * @return index of the address (existing index, or newly created index if not already registered) + */ + function register(address addr) external returns (uint256); /** - * @return size of address table (= first unused index) + * @return size of address table (= first unused index) */ - function size() external view returns(uint); + function size() external view returns (uint256); } diff --git a/solgen/src/precompiles/ArbAggregator.sol b/solgen/src/precompiles/ArbAggregator.sol index 14f19eba1a..5affdf08e2 100644 --- a/solgen/src/precompiles/ArbAggregator.sol +++ b/solgen/src/precompiles/ArbAggregator.sol @@ -18,7 +18,7 @@ pragma solidity >=0.4.21 <0.9.0; -/// @title Provides aggregators and their users methods for configuring how they participate in L1 aggregation. +/// @title Provides aggregators and their users methods for configuring how they participate in L1 aggregation. /// @notice Precompiled contract that exists in every Arbitrum chain at 0x000000000000000000000000000000000000006d interface ArbAggregator { /// @notice Get the preferred aggregator for an address. @@ -63,12 +63,12 @@ interface ArbAggregator { /// @notice Get the tx base fee (in approximate L1 gas) for aggregator /// @param aggregator The aggregator to get the base fee for - function getTxBaseFee(address aggregator) external view returns (uint); + function getTxBaseFee(address aggregator) external view returns (uint256); /// @notice Set the tx base fee (in approximate L1 gas) for aggregator /// Revert unless called by aggregator or the chain owner /// Revert if feeInL1Gas is outside the chain's allowed bounds /// @param aggregator The aggregator to set the fee for /// @param feeInL1Gas The base fee in L1 gas - function setTxBaseFee(address aggregator, uint feeInL1Gas) external; + function setTxBaseFee(address aggregator, uint256 feeInL1Gas) external; } diff --git a/solgen/src/precompiles/ArbBLS.sol b/solgen/src/precompiles/ArbBLS.sol index d0c2c5ce4f..1178ee22be 100644 --- a/solgen/src/precompiles/ArbBLS.sol +++ b/solgen/src/precompiles/ArbBLS.sol @@ -22,16 +22,42 @@ pragma solidity >=0.4.21 <0.9.0; /// @notice Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000067. interface ArbBLS { /// @notice Deprecated -- equivalent to registerAltBN128 - function register(uint x0, uint x1, uint y0, uint y1) external; // DEPRECATED + function register( + uint256 x0, + uint256 x1, + uint256 y0, + uint256 y1 + ) external; // DEPRECATED /// @notice Deprecated -- equivalent to getAltBN128 - function getPublicKey(address addr) external view returns (uint, uint, uint, uint); // DEPRECATED + function getPublicKey(address addr) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); // DEPRECATED /// @notice Associate an AltBN128 public key with the caller's address - function registerAltBN128(uint x0, uint x1, uint y0, uint y1) external; + function registerAltBN128( + uint256 x0, + uint256 x1, + uint256 y0, + uint256 y1 + ) external; /// @notice Get the AltBN128 public key associated with an address (revert if there isn't one) - function getAltBN128(address addr) external view returns (uint, uint, uint, uint); + function getAltBN128(address addr) + external + view + returns ( + uint256, + uint256, + uint256, + uint256 + ); /// @notice Associate a BLS 12-381 public key with the caller's address function registerBLS12381(bytes calldata key) external; diff --git a/solgen/src/precompiles/ArbDebug.sol b/solgen/src/precompiles/ArbDebug.sol index 15be9c2d8e..1061297e02 100644 --- a/solgen/src/precompiles/ArbDebug.sol +++ b/solgen/src/precompiles/ArbDebug.sol @@ -19,18 +19,30 @@ pragma solidity >=0.4.21 <0.9.0; /** -* @title A test contract whose methods are only accessible in debug mode -* @notice Precompiled contract that exists in every Arbitrum chain at 0x00000000000000000000000000000000000000ff. -*/ + * @title A test contract whose methods are only accessible in debug mode + * @notice Precompiled contract that exists in every Arbitrum chain at 0x00000000000000000000000000000000000000ff. + */ interface ArbDebug { /// @notice Caller becomes a chain owner function becomeChainOwner() external; - + /// @notice Emit events with values based on the args provided - function events(bool flag, bytes32 value) external payable returns(address, uint256); + function events(bool flag, bytes32 value) external payable returns (address, uint256); // Events that exist for testing log creation and pricing event Basic(bool flag, bytes32 indexed value); - event Mixed(bool indexed flag, bool not, bytes32 indexed value, address conn, address indexed caller); - event Store(bool indexed flag, address indexed field, uint24 number, bytes32 value, bytes store); + event Mixed( + bool indexed flag, + bool not, + bytes32 indexed value, + address conn, + address indexed caller + ); + event Store( + bool indexed flag, + address indexed field, + uint24 number, + bytes32 value, + bytes store + ); } diff --git a/solgen/src/precompiles/ArbFunctionTable.sol b/solgen/src/precompiles/ArbFunctionTable.sol index 42d1dda181..a5f1d08b74 100644 --- a/solgen/src/precompiles/ArbFunctionTable.sol +++ b/solgen/src/precompiles/ArbFunctionTable.sol @@ -18,10 +18,10 @@ pragma solidity >=0.4.21 <0.9.0; -/// @title Deprecated - Provided aggregator's the ability to manage function tables, -// this enables one form of transaction compression. -/// @notice The Nitro aggregator implementation does not use these, -// so these methods have been stubbed and their effects disabled. +/// @title Deprecated - Provided aggregator's the ability to manage function tables, +// this enables one form of transaction compression. +/// @notice The Nitro aggregator implementation does not use these, +// so these methods have been stubbed and their effects disabled. /// They are kept for backwards compatibility. /// Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000068. interface ArbFunctionTable { @@ -29,8 +29,15 @@ interface ArbFunctionTable { function upload(bytes calldata buf) external; /// @notice Returns the empty table's size, which is 0 - function size(address addr) external view returns(uint); + function size(address addr) external view returns (uint256); /// @notice No-op - function get(address addr, uint index) external view returns(uint, bool, uint); + function get(address addr, uint256 index) + external + view + returns ( + uint256, + bool, + uint256 + ); } diff --git a/solgen/src/precompiles/ArbGasInfo.sol b/solgen/src/precompiles/ArbGasInfo.sol index 2eccde2f95..4cb17f946e 100644 --- a/solgen/src/precompiles/ArbGasInfo.sol +++ b/solgen/src/precompiles/ArbGasInfo.sol @@ -18,8 +18,8 @@ pragma solidity >=0.4.21 <0.9.0; -/// @title Provides insight into the cost of using the chain. -/// @notice These methods have been adjusted to account for Nitro's heavy use of calldata compression. +/// @title Provides insight into the cost of using the chain. +/// @notice These methods have been adjusted to account for Nitro's heavy use of calldata compression. /// Of note to end-users, we no longer make a distinction between non-zero and zero-valued calldata bytes. /// Precompiled contract that exists in every Arbitrum chain at 0x000000000000000000000000000000000000006c. interface ArbGasInfo { @@ -33,7 +33,17 @@ interface ArbGasInfo { /// per ArbGas congestion, /// per ArbGas total /// ) - function getPricesInWeiWithAggregator(address aggregator) external view returns (uint, uint, uint, uint, uint, uint); + function getPricesInWeiWithAggregator(address aggregator) + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ); /// @notice Get gas prices. Uses the caller's preferred aggregator, or the default if the caller doesn't have a preferred one. /// @return return gas prices in wei @@ -45,32 +55,63 @@ interface ArbGasInfo { /// per ArbGas congestion, /// per ArbGas total /// ) - function getPricesInWei() external view returns (uint, uint, uint, uint, uint, uint); + function getPricesInWei() + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ); /// @notice Get prices in ArbGas for the supplied aggregator /// @return (per L2 tx, per L1 calldata unit, per storage allocation) - function getPricesInArbGasWithAggregator(address aggregator) external view returns (uint, uint, uint); + function getPricesInArbGasWithAggregator(address aggregator) + external + view + returns ( + uint256, + uint256, + uint256 + ); /// @notice Get prices in ArbGas. Assumes the callers preferred validator, or the default if caller doesn't have a preferred one. /// @return (per L2 tx, per L1 calldata unit, per storage allocation) - function getPricesInArbGas() external view returns (uint, uint, uint); + function getPricesInArbGas() + external + view + returns ( + uint256, + uint256, + uint256 + ); /// @notice Get the gas accounting parameters /// @return (speedLimitPerSecond, gasPoolMax, maxTxGasLimit) - function getGasAccountingParams() external view returns (uint, uint, uint); + function getGasAccountingParams() + external + view + returns ( + uint256, + uint256, + uint256 + ); /// @notice Get the minimum gas price needed for a tx to succeed - function getMinimumGasPrice() external view returns(uint); + function getMinimumGasPrice() external view returns (uint256); /// @notice Get the number of seconds worth of the speed limit the large gas pool contains - function getGasPoolSeconds() external view returns(uint); + function getGasPoolSeconds() external view returns (uint256); /// @notice Get the number of seconds worth of the speed limit the small gas pool contains - function getSmallGasPoolSeconds() external view returns(uint); + function getSmallGasPoolSeconds() external view returns (uint256); /// @notice Get ArbOS's estimate of the L1 gas price in wei - function getL1GasPriceEstimate() external view returns(uint); + function getL1GasPriceEstimate() external view returns (uint256); /// @notice Get L1 gas fees paid by the current transaction - function getCurrentTxL1GasFees() external view returns(uint); + function getCurrentTxL1GasFees() external view returns (uint256); } diff --git a/solgen/src/precompiles/ArbOwner.sol b/solgen/src/precompiles/ArbOwner.sol index 09b324d970..57be21579a 100644 --- a/solgen/src/precompiles/ArbOwner.sol +++ b/solgen/src/precompiles/ArbOwner.sol @@ -18,11 +18,10 @@ pragma solidity >=0.4.21 <0.9.0; - -/// @title Provides owners with tools for managing the rollup. +/// @title Provides owners with tools for managing the rollup. /// @notice Calls by non-owners will always revert. /// Most of Arbitrum Classic's owner methods have been removed since they no longer make sense in Nitro: -/// - What were once chain parameters are now parts of ArbOS's state, and those that remain are set at genesis. +/// - What were once chain parameters are now parts of ArbOS's state, and those that remain are set at genesis. /// - ArbOS upgrades happen with the rest of the system rather than being independent /// - Exemptions to address aliasing are no longer offered. Exemptions were intended to support backward compatibility for contracts deployed before aliasing was introduced, but no exemptions were ever requested. /// Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000070. @@ -34,13 +33,13 @@ interface ArbOwner { function removeChainOwner(address ownerToRemove) external; /// @notice See if the user is a chain owner - function isChainOwner(address addr) external view returns(bool); + function isChainOwner(address addr) external view returns (bool); /// @notice Retrieves the list of chain owners - function getAllChainOwners() external view returns(address[] memory); + function getAllChainOwners() external view returns (address[] memory); /// @notice Sets the L1 gas price estimate directly, bypassing the autoregression - function setL1GasPriceEstimate(uint priceInWei) external; + function setL1GasPriceEstimate(uint256 priceInWei) external; /// @notice Sets the L2 gas price directly, bypassing the pool calculus function setL2GasPrice(uint256 priceInWei) external; @@ -61,7 +60,7 @@ interface ArbOwner { function setMaxTxGasLimit(uint64 limit) external view; /// @notice Gets the network fee collector - function getNetworkFeeAccount() external view returns(address); + function getNetworkFeeAccount() external view returns (address); /// @notice Sets the network fee collector function setNetworkFeeAccount(address newNetworkFeeAccount) external view; diff --git a/solgen/src/precompiles/ArbOwnerPublic.sol b/solgen/src/precompiles/ArbOwnerPublic.sol index a241e4bbfe..db412e84e4 100644 --- a/solgen/src/precompiles/ArbOwnerPublic.sol +++ b/solgen/src/precompiles/ArbOwnerPublic.sol @@ -22,11 +22,11 @@ pragma solidity >=0.4.21 <0.9.0; /// @notice Precompiled contract that exists in every Arbitrum chain at 0x000000000000000000000000000000000000006b. interface ArbOwnerPublic { /// @notice See if the user is a chain owner - function isChainOwner(address addr) external view returns(bool); + function isChainOwner(address addr) external view returns (bool); /// @notice Retrieves the list of chain owners - function getAllChainOwners() external view returns(address[] memory); + function getAllChainOwners() external view returns (address[] memory); /// @notice Gets the network fee collector - function getNetworkFeeAccount() external view returns(address); + function getNetworkFeeAccount() external view returns (address); } diff --git a/solgen/src/precompiles/ArbRetryableTx.sol b/solgen/src/precompiles/ArbRetryableTx.sol index c086689685..1a42fd29fa 100644 --- a/solgen/src/precompiles/ArbRetryableTx.sol +++ b/solgen/src/precompiles/ArbRetryableTx.sol @@ -19,58 +19,64 @@ pragma solidity >=0.4.21 <0.9.0; /** -* @title Methods for managing retryables. -* @notice Precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006e -*/ + * @title Methods for managing retryables. + * @notice Precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006e + */ interface ArbRetryableTx { /** - * @notice Schedule an attempt to redeem a redeemable tx, donating all of the call's gas to the redeem. - * Revert if ticketId does not exist. - * @param ticketId unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) ) - * @return txId that the redeem attempt will have + * @notice Schedule an attempt to redeem a redeemable tx, donating all of the call's gas to the redeem. + * Revert if ticketId does not exist. + * @param ticketId unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) ) + * @return txId that the redeem attempt will have */ - function redeem(bytes32 ticketId) external returns(bytes32); + function redeem(bytes32 ticketId) external returns (bytes32); /** - * @notice Return the minimum lifetime of redeemable txn. - * @return lifetime in seconds - */ - function getLifetime() external view returns(uint); + * @notice Return the minimum lifetime of redeemable txn. + * @return lifetime in seconds + */ + function getLifetime() external view returns (uint256); /** - * @notice Return the timestamp when ticketId will age out, reverting if it does not exist - * @param ticketId unique ticket identifier - * @return timestamp for ticket's deadline - */ - function getTimeout(bytes32 ticketId) external view returns(uint); + * @notice Return the timestamp when ticketId will age out, reverting if it does not exist + * @param ticketId unique ticket identifier + * @return timestamp for ticket's deadline + */ + function getTimeout(bytes32 ticketId) external view returns (uint256); /** - * @notice Adds one lifetime period to the life of ticketId. - * Donate gas to pay for the lifetime extension. - * If successful, emits LifetimeExtended event. - * Revert if ticketId does not exist, or if the timeout of ticketId is already at least one lifetime period in the future. - * @param ticketId unique ticket identifier - * @return new timeout of ticketId - */ - function keepalive(bytes32 ticketId) external returns(uint); + * @notice Adds one lifetime period to the life of ticketId. + * Donate gas to pay for the lifetime extension. + * If successful, emits LifetimeExtended event. + * Revert if ticketId does not exist, or if the timeout of ticketId is already at least one lifetime period in the future. + * @param ticketId unique ticket identifier + * @return new timeout of ticketId + */ + function keepalive(bytes32 ticketId) external returns (uint256); /** - * @notice Return the beneficiary of ticketId. - * Revert if ticketId doesn't exist. - * @param ticketId unique ticket identifier - * @return address of beneficiary for ticket - */ + * @notice Return the beneficiary of ticketId. + * Revert if ticketId doesn't exist. + * @param ticketId unique ticket identifier + * @return address of beneficiary for ticket + */ function getBeneficiary(bytes32 ticketId) external view returns (address); /** - * @notice Cancel ticketId and refund its callvalue to its beneficiary. - * Revert if ticketId doesn't exist, or if called by anyone other than ticketId's beneficiary. - * @param ticketId unique ticket identifier - */ + * @notice Cancel ticketId and refund its callvalue to its beneficiary. + * Revert if ticketId doesn't exist, or if called by anyone other than ticketId's beneficiary. + * @param ticketId unique ticket identifier + */ function cancel(bytes32 ticketId) external; event TicketCreated(bytes32 indexed ticketId); - event LifetimeExtended(bytes32 indexed ticketId, uint newTimeout); - event RedeemScheduled(bytes32 indexed ticketId, bytes32 indexed retryTxHash, uint64 indexed sequenceNum, uint64 donatedGas, address gasDonor); + event LifetimeExtended(bytes32 indexed ticketId, uint256 newTimeout); + event RedeemScheduled( + bytes32 indexed ticketId, + bytes32 indexed retryTxHash, + uint64 indexed sequenceNum, + uint64 donatedGas, + address gasDonor + ); event Canceled(bytes32 indexed ticketId); } diff --git a/solgen/src/precompiles/ArbStatistics.sol b/solgen/src/precompiles/ArbStatistics.sol index 41be7a1805..689935b026 100644 --- a/solgen/src/precompiles/ArbStatistics.sol +++ b/solgen/src/precompiles/ArbStatistics.sol @@ -29,5 +29,15 @@ interface ArbStatistics { /// Number of transaction receipt issued, /// Number of contracts created, /// ) - function getStats() external view returns(uint, uint, uint, uint, uint, uint); + function getStats() + external + view + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ); } diff --git a/solgen/src/precompiles/ArbSys.sol b/solgen/src/precompiles/ArbSys.sol index 4416a7b538..248c533c30 100644 --- a/solgen/src/precompiles/ArbSys.sol +++ b/solgen/src/precompiles/ArbSys.sol @@ -19,45 +19,45 @@ pragma solidity >=0.4.21 <0.9.0; /** -* @title System level functionality -* @notice For use by contracts to interact with core L2-specific functionality. -* Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. + * @title System level functionality + * @notice For use by contracts to interact with core L2-specific functionality. + * Precompiled contract that exists in every Arbitrum chain at address(100), 0x0000000000000000000000000000000000000064. */ interface ArbSys { /** - * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) - * @return block number as int + * @notice Get Arbitrum block number (distinct from L1 block number; Arbitrum genesis block has block number 0) + * @return block number as int */ - function arbBlockNumber() external view returns (uint); + function arbBlockNumber() external view returns (uint256); /** - * @notice Get Arbitrum block hash (reverts unless currentBlockNum-256 <= arbBlockNum < currentBlockNum) - * @return block hash + * @notice Get Arbitrum block hash (reverts unless currentBlockNum-256 <= arbBlockNum < currentBlockNum) + * @return block hash */ - function arbBlockHash(uint arbBlockNum) external view returns (bytes32); + function arbBlockHash(uint256 arbBlockNum) external view returns (bytes32); /** - * @notice Gets the rollup's unique chain identifier - * @return Chain identifier as int + * @notice Gets the rollup's unique chain identifier + * @return Chain identifier as int */ - function arbChainID() external view returns(uint); + function arbChainID() external view returns (uint256); /** - * @notice Get internal version number identifying an ArbOS build - * @return version number as int + * @notice Get internal version number identifying an ArbOS build + * @return version number as int */ - function arbOSVersion() external view returns (uint); + function arbOSVersion() external view returns (uint256); /** * @notice Returns 0 since Nitro has no concept of storage gas * @return int 0 */ - function getStorageGasAvailable() external returns(uint); + function getStorageGasAvailable() external returns (uint256); /** - * @notice check if current call is coming from l1 - * @return true if the caller of this was called directly from L1 - */ + * @notice check if current call is coming from l1 + * @return true if the caller of this was called directly from L1 + */ function isTopLevelCall() external view returns (bool); /** @@ -66,7 +66,10 @@ interface ArbSys { * @param unused argument no longer used * @return aliased sender address */ - function mapL1SenderContractAddressToL2Alias(address sender, address unused) external pure returns(address); + function mapL1SenderContractAddressToL2Alias(address sender, address unused) + external + pure + returns (address); /** * @notice check if the caller (of this caller of this) is an aliased L1 contract address @@ -81,43 +84,53 @@ interface ArbSys { function myCallersAddressWithoutAliasing() external view returns (address); /** - * @notice Send given amount of Eth to dest from sender. - * This is a convenience function, which is equivalent to calling sendTxToL1 with empty data. - * @param destination recipient address on L1 - * @return unique identifier for this L2-to-L1 transaction. - */ - function withdrawEth(address destination) external payable returns(uint); + * @notice Send given amount of Eth to dest from sender. + * This is a convenience function, which is equivalent to calling sendTxToL1 with empty data. + * @param destination recipient address on L1 + * @return unique identifier for this L2-to-L1 transaction. + */ + function withdrawEth(address destination) external payable returns (uint256); /** - * @notice Send a transaction to L1 - * @param destination recipient address on L1 - * @param data (optional) calldata for L1 contract call - * @return a unique identifier for this L2-to-L1 transaction. - */ - function sendTxToL1(address destination, bytes calldata data) external payable returns(uint); + * @notice Send a transaction to L1 + * @param destination recipient address on L1 + * @param data (optional) calldata for L1 contract call + * @return a unique identifier for this L2-to-L1 transaction. + */ + function sendTxToL1(address destination, bytes calldata data) + external + payable + returns (uint256); /** - * @notice Get send Merkle tree state - * @return size number of sends in the history - * @return root root hash of the send history - * @return partials hashes of partial subtrees in the send history tree - */ - function sendMerkleTreeState() external view returns(uint size, bytes32 root, bytes32[] memory partials); + * @notice Get send Merkle tree state + * @return size number of sends in the history + * @return root root hash of the send history + * @return partials hashes of partial subtrees in the send history tree + */ + function sendMerkleTreeState() + external + view + returns ( + uint256 size, + bytes32 root, + bytes32[] memory partials + ); /** * @notice creates a send txn from L2 to L1 * @param position = (level << 192) + leaf = (0 << 192) + leaf = leaf - */ + */ event L2ToL1Transaction( address caller, address indexed destination, - uint indexed hash, - uint indexed position, - uint indexInBatch, - uint arbBlockNum, - uint ethBlockNum, - uint timestamp, - uint callvalue, + uint256 indexed hash, + uint256 indexed position, + uint256 indexInBatch, + uint256 arbBlockNum, + uint256 ethBlockNum, + uint256 timestamp, + uint256 callvalue, bytes data ); @@ -126,10 +139,10 @@ interface ArbSys { * @param reserved an index meant only to align the 4th index with L2ToL1Transaction's 4th event * @param hash the merkle hash * @param position = (level << 192) + leaf - */ + */ event SendMerkleUpdate( - uint indexed reserved, + uint256 indexed reserved, bytes32 indexed hash, - uint indexed position + uint256 indexed position ); } diff --git a/solgen/src/precompiles/ArbosTest.sol b/solgen/src/precompiles/ArbosTest.sol index e930c0a56e..c1dc590eb8 100644 --- a/solgen/src/precompiles/ArbosTest.sol +++ b/solgen/src/precompiles/ArbosTest.sol @@ -19,10 +19,10 @@ pragma solidity >=0.4.21 <0.9.0; /// @title Deprecated - Provides a method of burning arbitrary amounts of gas, -/// @notice This exists for historical reasons. Pre-Nitro, `ArbosTest` had additional methods only the zero address could call. +/// @notice This exists for historical reasons. Pre-Nitro, `ArbosTest` had additional methods only the zero address could call. /// These have been removed since users don't use them and calls to missing methods revert. /// Precompiled contract that exists in every Arbitrum chain at 0x0000000000000000000000000000000000000069. interface ArbosTest { /// @notice Unproductively burns the amount of L2 ArbGas - function burnArbGas(uint gasAmount) external pure; + function burnArbGas(uint256 gasAmount) external pure; } diff --git a/solgen/src/rollup/IRollupCore.sol b/solgen/src/rollup/IRollupCore.sol index f066e53348..1509b663f6 100644 --- a/solgen/src/rollup/IRollupCore.sol +++ b/solgen/src/rollup/IRollupCore.sol @@ -46,11 +46,7 @@ interface IRollupCore { uint256 inboxMaxCount ); - event NodeConfirmed( - uint64 indexed nodeNum, - bytes32 blockHash, - bytes32 sendRoot - ); + event NodeConfirmed(uint64 indexed nodeNum, bytes32 blockHash, bytes32 sendRoot); event NodeRejected(uint64 indexed nodeNum); diff --git a/solgen/src/rollup/IRollupLogic.sol b/solgen/src/rollup/IRollupLogic.sol index 816c6f08a2..c82bdcfee0 100644 --- a/solgen/src/rollup/IRollupLogic.sol +++ b/solgen/src/rollup/IRollupLogic.sol @@ -40,6 +40,7 @@ interface IRollupUserAbs { interface IRollupUser is IRollupUserAbs { function newStakeOnExistingNode(uint64 nodeNum, bytes32 nodeHash) external payable; + function newStakeOnNewNode( RollupLib.Assertion calldata assertion, bytes32 expectedNodeHash, @@ -48,7 +49,11 @@ interface IRollupUser is IRollupUserAbs { } interface IRollupUserERC20 is IRollupUserAbs { - function newStakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external; + function newStakeOnExistingNode( + uint256 tokenAmount, + uint64 nodeNum, + bytes32 nodeHash + ) external; function newStakeOnNewNode( uint256 tokenAmount, @@ -61,10 +66,8 @@ interface IRollupUserERC20 is IRollupUserAbs { interface IRollupAdmin { event OwnerFunctionCalled(uint256 indexed id); - function initialize( - Config calldata config, - ContractDependencies calldata connectedContracts - ) external; + function initialize(Config calldata config, ContractDependencies calldata connectedContracts) + external; /** * @notice Add a contract authorized to put messages into this rollup's inbox @@ -102,8 +105,7 @@ interface IRollupAdmin { * @param _validator addresses to set in the whitelist * @param _val value to set in the whitelist for corresponding address */ - function setValidator(address[] memory _validator, bool[] memory _val) - external; + function setValidator(address[] memory _validator, bool[] memory _val) external; /** * @notice Set a new owner address for the rollup proxy @@ -165,10 +167,7 @@ interface IRollupAdmin { */ function upgradeBeacon(address beacon, address newImplementation) external; - function forceResolveChallenge( - address[] memory stackerA, - address[] memory stackerB - ) external; + function forceResolveChallenge(address[] memory stackerA, address[] memory stackerB) external; function forceRefundStaker(address[] memory stacker) external; diff --git a/solgen/src/rollup/RollupAdminLogic.sol b/solgen/src/rollup/RollupAdminLogic.sol index 440870e7f9..3db563e1e4 100644 --- a/solgen/src/rollup/RollupAdminLogic.sol +++ b/solgen/src/rollup/RollupAdminLogic.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import { IRollupAdmin, IRollupUser } from "./IRollupLogic.sol"; +import {IRollupAdmin, IRollupUser} from "./IRollupLogic.sol"; import "./RollupCore.sol"; import "../bridge/IOutbox.sol"; import "../bridge/ISequencerInbox.sol"; @@ -13,10 +13,12 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol"; import {NO_CHAL_INDEX} from "../libraries/Constants.sol"; contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgradeable { - function initialize( - Config calldata config, - ContractDependencies calldata connectedContracts - ) external override onlyProxy initializer { + function initialize(Config calldata config, ContractDependencies calldata connectedContracts) + external + override + onlyProxy + initializer + { delayedBridge = connectedContracts.delayedBridge; sequencerBridge = connectedContracts.sequencerInbox; outbox = connectedContracts.outbox; @@ -54,17 +56,10 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade emit RollupInitialized(config.wasmModuleRoot, config.chainId); } - function createInitialNode() - private - view - returns (Node memory) - { + function createInitialNode() private view returns (Node memory) { GlobalState memory emptyGlobalState; bytes32 state = RollupLib.stateHashMem( - RollupLib.ExecutionState( - emptyGlobalState, - MachineStatus.FINISHED - ), + RollupLib.ExecutionState(emptyGlobalState, MachineStatus.FINISHED), 1 // inboxMaxCount - force the first assertion to read a message ); return @@ -213,7 +208,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade * To change the stake token without breaking consistency one would need to: * Pause the system, have all stakers remove their funds, * update the user logic to handle ERC20s, change the stake token, then resume. - * + * * Note: To avoid loss of funds stakers must remove their funds and claim all the * available withdrawable funds before the system is paused. */ @@ -231,7 +226,9 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade * @notice Set max delay for sequencer inbox * @param maxTimeVariation the maximum time variation parameters */ - function setSequencerInboxMaxTimeVariation(ISequencerInbox.MaxTimeVariation calldata maxTimeVariation) external override { + function setSequencerInboxMaxTimeVariation( + ISequencerInbox.MaxTimeVariation calldata maxTimeVariation + ) external override { sequencerBridge.setMaxTimeVariation(maxTimeVariation); emit OwnerFunctionCalled(14); } @@ -289,12 +286,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade ) external override whenPaused { require(prevNode == latestConfirmed(), "ONLY_LATEST_CONFIRMED"); - createNewNode( - assertion, - prevNode, - prevNodeInboxMaxCount, - expectedNodeHash - ); + createNewNode(assertion, prevNode, prevNodeInboxMaxCount, expectedNodeHash); emit OwnerFunctionCalled(23); } @@ -305,11 +297,7 @@ contract RollupAdminLogic is RollupCore, IRollupAdmin, SecondaryLogicUUPSUpgrade bytes32 sendRoot ) external override whenPaused { // this skips deadline, staker and zombie validation - confirmNode( - nodeNum, - blockHash, - sendRoot - ); + confirmNode(nodeNum, blockHash, sendRoot); emit OwnerFunctionCalled(24); } diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 97eede823f..4b35dab653 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -83,35 +83,21 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param nodeNum Index of the node * @return Node struct */ - function getNodeStorage(uint64 nodeNum) - internal - view - returns (Node storage) - { + function getNodeStorage(uint64 nodeNum) internal view returns (Node storage) { return _nodes[nodeNum]; } /** * @notice Get the Node for the given index. */ - function getNode(uint64 nodeNum) - public - view - override - returns (Node memory) - { + function getNode(uint64 nodeNum) public view override returns (Node memory) { return getNodeStorage(nodeNum); } /** * @notice Check if the specified node has been staked on by the provided staker */ - function nodeHasStaker(uint64 nodeNum, address staker) - public - view - override - returns (bool) - { + function nodeHasStaker(uint64 nodeNum, address staker) public view override returns (bool) { return _nodeStakers[nodeNum][staker]; } @@ -120,12 +106,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param stakerNum Index of the staker * @return Address of the staker */ - function getStakerAddress(uint64 stakerNum) - external - view - override - returns (address) - { + function getStakerAddress(uint64 stakerNum) external view override returns (address) { return _stakerList[stakerNum]; } @@ -143,12 +124,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param staker Staker address to lookup * @return Latest node staked of the staker */ - function latestStakedNode(address staker) - public - view - override - returns (uint64) - { + function latestStakedNode(address staker) public view override returns (uint64) { return _stakerMap[staker].latestStakedNode; } @@ -157,12 +133,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param staker Staker address to lookup * @return Current challenge of the staker */ - function currentChallenge(address staker) - public - view - override - returns (uint64) - { + function currentChallenge(address staker) public view override returns (uint64) { return _stakerMap[staker].currentChallenge; } @@ -171,12 +142,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param staker Staker address to lookup * @return Amount staked of the staker */ - function amountStaked(address staker) - public - view - override - returns (uint256) - { + function amountStaked(address staker) public view override returns (uint256) { return _stakerMap[staker].amountStaked; } @@ -194,12 +160,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param zombieNum Index of the zombie to lookup * @return Original staker address of the zombie */ - function zombieAddress(uint256 zombieNum) - public - view - override - returns (address) - { + function zombieAddress(uint256 zombieNum) public view override returns (address) { return _zombies[zombieNum].stakerAddress; } @@ -208,12 +169,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param zombieNum Index of the zombie to lookup * @return Latest node that the given zombie is staked on */ - function zombieLatestStakedNode(uint256 zombieNum) - public - view - override - returns (uint64) - { + function zombieLatestStakedNode(uint256 zombieNum) public view override returns (uint64) { return _zombies[zombieNum].latestStakedNode; } @@ -236,12 +192,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param user Address to check the funds of * @return Amount of funds withdrawable by user */ - function withdrawableFunds(address user) - external - view - override - returns (uint256) - { + function withdrawableFunds(address user) external view override returns (uint256) { return _withdrawableFunds[user]; } @@ -304,10 +255,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { ) internal { Node storage node = getNodeStorage(nodeNum); // Authenticate data against node's confirm data pre-image - require( - node.confirmData == RollupLib.confirmHash(blockHash, sendRoot), - "CONFIRM_DATA" - ); + require(node.confirmData == RollupLib.confirmHash(blockHash, sendRoot), "CONFIRM_DATA"); // trusted external call to outbox outbox.updateSendRoot(sendRoot, blockHash); @@ -324,9 +272,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param stakerAddress Address of the new staker * @param depositAmount Stake amount of the new staker */ - function createNewStake(address stakerAddress, uint256 depositAmount) - internal - { + function createNewStake(address stakerAddress, uint256 depositAmount) internal { uint64 stakerIndex = uint64(_stakerList.length); _stakerList.push(stakerAddress); _stakerMap[stakerAddress] = Staker( @@ -388,9 +334,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param stakerAddress Address of the staker to increase the stake of * @param amountAdded Amount of stake to add to the staker */ - function increaseStakeBy(address stakerAddress, uint256 amountAdded) - internal - { + function increaseStakeBy(address stakerAddress, uint256 amountAdded) internal { Staker storage staker = _stakerMap[stakerAddress]; uint256 initialStaked = staker.amountStaked; uint256 finalStaked = initialStaked + amountAdded; @@ -404,10 +348,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param target Amount of stake to leave with the staker * @return Amount of value released from the stake */ - function reduceStakeTo(address stakerAddress, uint256 target) - internal - returns (uint256) - { + function reduceStakeTo(address stakerAddress, uint256 target) internal returns (uint256) { Staker storage staker = _stakerMap[stakerAddress]; uint256 current = staker.amountStaked; require(target <= current, "TOO_LITTLE_STAKE"); @@ -433,9 +374,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param zombieNum Index of the zombie to move * @param latest New latest node the zombie is staked on */ - function zombieUpdateLatestStakedNode(uint256 zombieNum, uint64 latest) - internal - { + function zombieUpdateLatestStakedNode(uint256 zombieNum, uint64 latest) internal { _zombies[zombieNum].latestStakedNode = latest; } @@ -453,10 +392,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @param staker Address of the staker to mark * @return The number of stakers after adding this one */ - function addStaker(uint64 nodeNum, address staker) - internal - returns (uint256) - { + function addStaker(uint64 nodeNum, address staker) internal returns (uint256) { require(!_nodeStakers[nodeNum][staker], "ALREADY_STAKED"); _nodeStakers[nodeNum][staker] = true; Node storage node = getNodeStorage(nodeNum); @@ -502,9 +438,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { staker.latestStakedNode = nodeNum; if (newStakerCount == 1) { Node storage parent = getNodeStorage(nodeNum); - parent.newChildConfirmDeadline( - uint64(block.number) + confirmPeriodBlocks - ); + parent.newChildConfirmDeadline(uint64(block.number) + confirmPeriodBlocks); } } @@ -525,18 +459,12 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { * @notice Increase the withdrawable funds for the given address * @param account Address of the account to add withdrawable funds to */ - function increaseWithdrawableFunds(address account, uint256 amount) - internal - { + function increaseWithdrawableFunds(address account, uint256 amount) internal { uint256 initialWithdrawable = _withdrawableFunds[account]; uint256 finalWithdrawable = initialWithdrawable + amount; _withdrawableFunds[account] = finalWithdrawable; totalWithdrawableFunds += amount; - emit UserWithdrawableFundsUpdated( - account, - initialWithdrawable, - finalWithdrawable - ); + emit UserWithdrawableFundsUpdated(account, initialWithdrawable, finalWithdrawable); } /** @@ -587,21 +515,15 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { // Make sure the previous state is correct against the node being built on require( - RollupLib.stateHash( - assertion.beforeState, - prevNodeInboxMaxCount - ) == memoryFrame.prevNode.stateHash, + RollupLib.stateHash(assertion.beforeState, prevNodeInboxMaxCount) == + memoryFrame.prevNode.stateHash, "PREV_STATE_HASH" ); // Ensure that the assertion doesn't read past the end of the current inbox - uint256 afterInboxCount = assertion - .afterState - .globalState - .getInboxPosition(); + uint256 afterInboxCount = assertion.afterState.globalState.getInboxPosition(); require( - afterInboxCount >= - assertion.beforeState.globalState.getInboxPosition(), + afterInboxCount >= assertion.beforeState.globalState.getInboxPosition(), "INBOX_BACKWARDS" ); // See validator/assertion.go ExecutionState RequiredBatches() for reasoning @@ -612,31 +534,23 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { // The current inbox message was read afterInboxCount++; } - require( - afterInboxCount <= memoryFrame.currentInboxSize, - "INBOX_PAST_END" - ); + require(afterInboxCount <= memoryFrame.currentInboxSize, "INBOX_PAST_END"); // This gives replay protection against the state of the inbox if (afterInboxCount > 0) { - memoryFrame.sequencerBatchAcc = sequencerBridge.inboxAccs( - afterInboxCount - 1 - ); + memoryFrame.sequencerBatchAcc = sequencerBridge.inboxAccs(afterInboxCount - 1); } } { memoryFrame.executionHash = RollupLib.executionHash(assertion); - memoryFrame.deadlineBlock = - uint64(block.number) + - confirmPeriodBlocks; + memoryFrame.deadlineBlock = uint64(block.number) + confirmPeriodBlocks; memoryFrame.hasSibling = memoryFrame.prevNode.latestChildNumber > 0; // here we don't use ternacy operator to remain compatible with slither if (memoryFrame.hasSibling) { - memoryFrame.lastHash = getNodeStorage( - memoryFrame.prevNode.latestChildNumber - ).nodeHash; + memoryFrame.lastHash = getNodeStorage(memoryFrame.prevNode.latestChildNumber) + .nodeHash; } else { memoryFrame.lastHash = memoryFrame.prevNode.nodeHash; } @@ -650,10 +564,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { require(newNodeHash == expectedNodeHash, "UNEXPECTED_NODE_HASH"); memoryFrame.node = NodeLib.createNode( - RollupLib.stateHash( - assertion.afterState, - memoryFrame.currentInboxSize - ), + RollupLib.stateHash(assertion.afterState, memoryFrame.currentInboxSize), RollupLib.challengeRootHash( memoryFrame.executionHash, block.number, diff --git a/solgen/src/rollup/RollupCreator.sol b/solgen/src/rollup/RollupCreator.sol index 40b2eb3fa6..5c6b92641d 100644 --- a/solgen/src/rollup/RollupCreator.sol +++ b/solgen/src/rollup/RollupCreator.sol @@ -37,7 +37,13 @@ import "../bridge/IBridge.sol"; import "./RollupLib.sol"; contract RollupCreator is Ownable { - event RollupCreated(address indexed rollupAddress, address inboxAddress, address adminProxy, address sequencerInbox, address delayedBridge); + event RollupCreated( + address indexed rollupAddress, + address inboxAddress, + address adminProxy, + address sequencerInbox, + address delayedBridge + ); event TemplatesUpdated(); BridgeCreator public bridgeCreator; @@ -51,7 +57,7 @@ contract RollupCreator is Ownable { function setTemplates( BridgeCreator _bridgeCreator, IOneStepProofEntry _osp, - IChallengeManager _challengeManagerLogic, + IChallengeManager _challengeManagerLogic, IRollupAdmin _rollupAdminLogic, IRollupUser _rollupUserLogic ) external onlyOwner { @@ -78,7 +84,10 @@ contract RollupCreator is Ownable { // RollupOwner should be the owner of Rollup's ProxyAdmin // RollupOwner should be the owner of Rollup // Bridge should have a single inbox and outbox - function createRollup(Config memory config, address expectedRollupAddr) external returns (address) { + function createRollup(Config memory config, address expectedRollupAddr) + external + returns (address) + { CreateRollupFrame memory frame; frame.admin = new ProxyAdmin(); @@ -88,13 +97,23 @@ contract RollupCreator is Ownable { frame.inbox, frame.rollupEventBridge, frame.outbox - ) = bridgeCreator.createBridge(address(frame.admin), expectedRollupAddr, config.sequencerInboxMaxTimeVariation); + ) = bridgeCreator.createBridge( + address(frame.admin), + expectedRollupAddr, + config.sequencerInboxMaxTimeVariation + ); frame.admin.transferOwnership(config.owner); - IChallengeManager challengeManager = IChallengeManager(address( - new TransparentUpgradeableProxy(address(challengeManagerTemplate), address(frame.admin), "") - )); + IChallengeManager challengeManager = IChallengeManager( + address( + new TransparentUpgradeableProxy( + address(challengeManagerTemplate), + address(frame.admin), + "" + ) + ) + ); challengeManager.initialize( IChallengeResultReceiver(expectedRollupAddr), frame.sequencerInbox, @@ -116,7 +135,13 @@ contract RollupCreator is Ownable { ); require(address(frame.rollup) == expectedRollupAddr, "WRONG_ROLLUP_ADDR"); - emit RollupCreated(address(frame.rollup), address(frame.inbox), address(frame.admin), address(frame.sequencerInbox), address(frame.delayedBridge)); + emit RollupCreated( + address(frame.rollup), + address(frame.inbox), + address(frame.admin), + address(frame.sequencerInbox), + address(frame.delayedBridge) + ); return address(frame.rollup); } } diff --git a/solgen/src/rollup/RollupEventBridge.sol b/solgen/src/rollup/RollupEventBridge.sol index edd85b192a..f0985dd16b 100644 --- a/solgen/src/rollup/RollupEventBridge.sol +++ b/solgen/src/rollup/RollupEventBridge.sol @@ -23,10 +23,7 @@ import "./IRollupLogic.sol"; import "../bridge/IBridge.sol"; import "../bridge/IMessageProvider.sol"; import "../libraries/DelegateCallAware.sol"; -import { - ROLLUP_PROTOCOL_EVENT_TYPE, - INITIALIZATION_MSG_TYPE -} from "../libraries/MessageTypes.sol"; +import {ROLLUP_PROTOCOL_EVENT_TYPE, INITIALIZATION_MSG_TYPE} from "../libraries/MessageTypes.sol"; /** * @title The inbox for rollup protocol events @@ -51,14 +48,8 @@ contract RollupEventBridge is IMessageProvider, DelegateCallAware { rollup = _rollup; } - function rollupInitialized( - address owner, - uint256 chainId - ) external onlyRollup { - bytes memory initMsg = abi.encodePacked( - owner, - chainId - ); + function rollupInitialized(address owner, uint256 chainId) external onlyRollup { + bytes memory initMsg = abi.encodePacked(owner, chainId); uint256 num = bridge.enqueueDelayedMessage( INITIALIZATION_MSG_TYPE, address(0), diff --git a/solgen/src/rollup/RollupLib.sol b/solgen/src/rollup/RollupLib.sol index e28d59027a..e8f02a16b9 100644 --- a/solgen/src/rollup/RollupLib.sol +++ b/solgen/src/rollup/RollupLib.sol @@ -46,7 +46,6 @@ struct ContractDependencies { IOutbox outbox; RollupEventBridge rollupEventBridge; IChallengeManager challengeManager; - IRollupAdmin rollupAdminLogic; IRollupUser rollupUserLogic; } @@ -96,11 +95,7 @@ library RollupLib { uint64 numBlocks; } - function executionHash(Assertion memory assertion) - internal - pure - returns (bytes32) - { + function executionHash(Assertion memory assertion) internal pure returns (bytes32) { MachineStatus[2] memory statuses; statuses[0] = assertion.beforeState.machineStatus; statuses[1] = assertion.afterState.machineStatus; @@ -111,22 +106,15 @@ library RollupLib { return executionHash(statuses, globalStates, assertion.numBlocks); } - function executionHash(MachineStatus[2] memory statuses, GlobalState[2] memory globalStates, uint64 numBlocks) - internal - pure - returns (bytes32) - { + function executionHash( + MachineStatus[2] memory statuses, + GlobalState[2] memory globalStates, + uint64 numBlocks + ) internal pure returns (bytes32) { bytes32[] memory segments = new bytes32[](2); - segments[0] = ChallengeLib.blockStateHash( - statuses[0], - globalStates[0].hash() - ); - segments[1] = ChallengeLib.blockStateHash( - statuses[1], - globalStates[1].hash() - ); - return - ChallengeLib.hashChallengeState(0, numBlocks, segments); + segments[0] = ChallengeLib.blockStateHash(statuses[0], globalStates[0].hash()); + segments[1] = ChallengeLib.blockStateHash(statuses[1], globalStates[1].hash()); + return ChallengeLib.hashChallengeState(0, numBlocks, segments); } function challengeRootHash( @@ -134,21 +122,10 @@ library RollupLib { uint256 proposedTime, bytes32 wasmModuleRoot ) internal pure returns (bytes32) { - return - keccak256( - abi.encodePacked( - execution, - proposedTime, - wasmModuleRoot - ) - ); + return keccak256(abi.encodePacked(execution, proposedTime, wasmModuleRoot)); } - function confirmHash(Assertion memory assertion) - internal - pure - returns (bytes32) - { + function confirmHash(Assertion memory assertion) internal pure returns (bytes32) { return confirmHash( assertion.afterState.globalState.getBlockHash(), @@ -156,11 +133,7 @@ library RollupLib { ); } - function confirmHash(bytes32 blockHash, bytes32 sendRoot) - internal - pure - returns (bytes32) - { + function confirmHash(bytes32 blockHash, bytes32 sendRoot) internal pure returns (bytes32) { return keccak256(abi.encodePacked(blockHash, sendRoot)); } @@ -171,14 +144,6 @@ library RollupLib { bytes32 inboxAcc ) internal pure returns (bytes32) { uint8 hasSiblingInt = hasSibling ? 1 : 0; - return - keccak256( - abi.encodePacked( - hasSiblingInt, - lastHash, - assertionExecHash, - inboxAcc - ) - ); + return keccak256(abi.encodePacked(hasSiblingInt, lastHash, assertionExecHash, inboxAcc)); } } diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index c6b164c61e..88632ca611 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { IRollupUser } from "./IRollupLogic.sol"; +import {IRollupUser} from "./IRollupLogic.sol"; import "../libraries/UUPSNotUpgradeable.sol"; import "./RollupCore.sol"; @@ -30,17 +30,11 @@ abstract contract AbsRollupUserLogic is * @notice Reject the next unresolved node * @param stakerAddress Example staker staked on sibling, used to prove a node is on an unconfirmable branch and can be rejected */ - function rejectNextNode(address stakerAddress) - external - onlyValidator - whenNotPaused - { + function rejectNextNode(address stakerAddress) external onlyValidator whenNotPaused { requireUnresolvedExists(); uint64 latestConfirmedNodeNum = latestConfirmed(); uint64 firstUnresolvedNodeNum = firstUnresolvedNode(); - Node storage firstUnresolvedNode_ = getNodeStorage( - firstUnresolvedNodeNum - ); + Node storage firstUnresolvedNode_ = getNodeStorage(firstUnresolvedNodeNum); if (firstUnresolvedNode_.prevNum == latestConfirmedNodeNum) { /**If the first unresolved node is a child of the latest confirmed node, to prove it can be rejected, we show: @@ -57,24 +51,19 @@ abstract contract AbsRollupUserLogic is requireUnresolved(latestStakedNode(stakerAddress)); // 3. staker isn't staked on first unresolved node; this proves staker's latest staked can't be a child of firstUnresolvedNode (recall staking on node requires staking on all of its parents) - require( - !nodeHasStaker(firstUnresolvedNodeNum, stakerAddress), - "STAKED_ON_TARGET" - ); + require(!nodeHasStaker(firstUnresolvedNodeNum, stakerAddress), "STAKED_ON_TARGET"); // If a staker is staked on a node that is neither a child nor a parent of firstUnresolvedNode, it must be a sibling, QED // Verify the block's deadline has passed firstUnresolvedNode_.requirePastDeadline(); - getNodeStorage(latestConfirmedNodeNum) - .requirePastChildConfirmDeadline(); + getNodeStorage(latestConfirmedNodeNum).requirePastChildConfirmDeadline(); removeOldZombies(0); // Verify that no staker is staked on this node require( - firstUnresolvedNode_.stakerCount == - countStakedZombies(firstUnresolvedNodeNum), + firstUnresolvedNode_.stakerCount == countStakedZombies(firstUnresolvedNodeNum), "HAS_STAKERS" ); } @@ -113,10 +102,7 @@ abstract contract AbsRollupUserLogic is removeOldZombies(0); // All non-zombie stakers are staked on this node - require( - node.stakerCount == stakerCount() + countStakedZombies(nodeNum), - "NOT_ALL_STAKED" - ); + require(node.stakerCount == stakerCount() + countStakedZombies(nodeNum), "NOT_ALL_STAKED"); confirmNode(nodeNum, blockHash, sendRoot); } @@ -125,11 +111,7 @@ abstract contract AbsRollupUserLogic is * @notice Create a new stake * @param depositAmount The amount of either eth or tokens staked */ - function _newStake(uint256 depositAmount) - internal - onlyValidator - whenNotPaused - { + function _newStake(uint256 depositAmount) internal onlyValidator whenNotPaused { // Verify that sender is not already a staker require(!isStaked(msg.sender), "ALREADY_STAKED"); require(!isZombie(msg.sender), "STAKER_IS_ZOMBIE"); @@ -158,10 +140,7 @@ abstract contract AbsRollupUserLogic is ); Node storage node = getNodeStorage(nodeNum); require(node.nodeHash == nodeHash, "NODE_REORG"); - require( - latestStakedNode(msg.sender) == node.prevNum, - "NOT_STAKED_PREV" - ); + require(latestStakedNode(msg.sender) == node.prevNum, "NOT_STAKED_PREV"); stakeOnNode(msg.sender, nodeNum); } @@ -180,8 +159,7 @@ abstract contract AbsRollupUserLogic is uint64 prevNode = latestStakedNode(msg.sender); { - uint256 timeSinceLastNode = block.number - - getNode(prevNode).createdAtBlock; + uint256 timeSinceLastNode = block.number - getNode(prevNode).createdAtBlock; // Verify that assertion meets the minimum Delta time requirement require(timeSinceLastNode >= minimumAssertionPeriod, "TIME_DELTA"); @@ -191,15 +169,11 @@ abstract contract AbsRollupUserLogic is // as it can't consume future batches. require( assertion.afterState.machineStatus == MachineStatus.ERRORED || - assertion.afterState.globalState.getInboxPosition() >= - prevNodeInboxMaxCount, + assertion.afterState.globalState.getInboxPosition() >= prevNodeInboxMaxCount, "TOO_SMALL" ); // Minimum size requirement: any assertion must contain at least one block - require( - assertion.numBlocks > 0, - "EMPTY_ASSERTION" - ); + require(assertion.numBlocks > 0, "EMPTY_ASSERTION"); // The rollup cannot advance normally from an errored state require( @@ -219,16 +193,8 @@ abstract contract AbsRollupUserLogic is * and move it to the desired node. * @param stakerAddress Address of the staker whose stake is refunded */ - function returnOldDeposit(address stakerAddress) - external - override - onlyValidator - whenNotPaused - { - require( - latestStakedNode(stakerAddress) <= latestConfirmed(), - "TOO_RECENT" - ); + function returnOldDeposit(address stakerAddress) external override onlyValidator whenNotPaused { + require(latestStakedNode(stakerAddress) <= latestConfirmed(), "TOO_RECENT"); requireUnchallengedStaker(stakerAddress); withdrawStaker(stakerAddress); } @@ -251,11 +217,7 @@ abstract contract AbsRollupUserLogic is * @notice Reduce the amount staked for the sender (difference between initial amount staked and target is creditted back to the sender). * @param target Target amount of stake for the staker. If this is below the current minimum, it will be set to minimum instead */ - function reduceDeposit(uint256 target) - external - onlyValidator - whenNotPaused - { + function reduceDeposit(uint256 target) external onlyValidator whenNotPaused { requireUnchallengedStaker(msg.sender); uint256 currentRequired = currentRequiredStake(); if (target < currentRequired) { @@ -306,11 +268,7 @@ abstract contract AbsRollupUserLogic is require( node1.challengeHash == RollupLib.challengeRootHash( - RollupLib.executionHash( - machineStatuses, - globalStates, - numBlocks - ), + RollupLib.executionHash(machineStatuses, globalStates, numBlocks), proposedTimes[0], wasmModuleRoots[0] ), @@ -350,12 +308,7 @@ abstract contract AbsRollupUserLogic is challengeStarted(stakers[0], stakers[1], challengeIndex); - emit RollupChallengeStarted( - challengeIndex, - stakers[0], - stakers[1], - nodeNums[0] - ); + emit RollupChallengeStarted(challengeIndex, stakers[0], stakers[1], nodeNums[0]); } function createChallengeHelper( @@ -385,20 +338,18 @@ abstract contract AbsRollupUserLogic is * @param winningStaker Address of the winning staker * @param losingStaker Address of the losing staker */ - function completeChallenge(uint256 challengeIndex, address winningStaker, address losingStaker) - external - override - whenNotPaused - { + function completeChallenge( + uint256 challengeIndex, + address winningStaker, + address losingStaker + ) external override whenNotPaused { // Only the challenge manager contract can call this to declare the winner and loser require(msg.sender == address(challengeManager), "WRONG_SENDER"); - require (challengeIndex == inChallenge(winningStaker, losingStaker)); + require(challengeIndex == inChallenge(winningStaker, losingStaker)); completeChallengeImpl(winningStaker, losingStaker); } - function completeChallengeImpl(address winningStaker, address losingStaker) - private - { + function completeChallengeImpl(address winningStaker, address losingStaker) private { uint256 remainingLoserStake = amountStaked(losingStaker); uint256 winnerStake = amountStaked(winningStaker); if (remainingLoserStake > winnerStake) { @@ -449,11 +400,7 @@ abstract contract AbsRollupUserLogic is * @notice Remove any zombies whose latest stake is earlier than the first unresolved node * @param startIndex Index in the zombie list to start removing zombies from (to limit the cost of this transaction) */ - function removeOldZombies(uint256 startIndex) - public - onlyValidator - whenNotPaused - { + function removeOldZombies(uint256 startIndex) public onlyValidator whenNotPaused { uint256 currentZombieCount = zombieCount(); uint256 firstUnresolved = firstUnresolvedNode(); for (uint256 i = startIndex; i < currentZombieCount; i++) { @@ -482,9 +429,7 @@ abstract contract AbsRollupUserLogic is if (_firstUnresolvedNodeNum - 1 == _latestCreatedNode) { return baseStake; } - uint256 firstUnresolvedDeadline = getNodeStorage( - _firstUnresolvedNodeNum - ).deadlineBlock; + uint256 firstUnresolvedDeadline = getNodeStorage(_firstUnresolvedNodeNum).deadlineBlock; if (_blockNumber < firstUnresolvedDeadline) { return baseStake; } @@ -547,23 +492,13 @@ abstract contract AbsRollupUserLogic is uint64 firstUnresolvedNodeNum, uint64 latestCreatedNode ) external view returns (uint256) { - return - currentRequiredStake( - blockNumber, - firstUnresolvedNodeNum, - latestCreatedNode - ); + return currentRequiredStake(blockNumber, firstUnresolvedNodeNum, latestCreatedNode); } function currentRequiredStake() public view returns (uint256) { uint64 firstUnresolvedNodeNum = firstUnresolvedNode(); - return - currentRequiredStake( - block.number, - firstUnresolvedNodeNum, - latestNodeCreated() - ); + return currentRequiredStake(block.number, firstUnresolvedNodeNum, latestNodeCreated()); } /** @@ -576,12 +511,7 @@ abstract contract AbsRollupUserLogic is * @param nodeNum The node on which to count staked zombies * @return The number of zombies staked on the node */ - function countStakedZombies(uint64 nodeNum) - public - view - override - returns (uint256) - { + function countStakedZombies(uint64 nodeNum) public view override returns (uint256) { uint256 currentZombieCount = zombieCount(); uint256 stakedZombieCount = 0; for (uint256 i = 0; i < currentZombieCount; i++) { @@ -598,8 +528,7 @@ abstract contract AbsRollupUserLogic is function requireUnresolvedExists() public view override { uint256 firstUnresolved = firstUnresolvedNode(); require( - firstUnresolved > latestConfirmed() && - firstUnresolved <= latestNodeCreated(), + firstUnresolved > latestConfirmed() && firstUnresolved <= latestNodeCreated(), "NO_UNRESOLVED" ); } @@ -615,16 +544,10 @@ abstract contract AbsRollupUserLogic is */ function requireUnchallengedStaker(address stakerAddress) private view { require(isStaked(stakerAddress), "NOT_STAKED"); - require( - currentChallenge(stakerAddress) == NO_CHAL_INDEX, - "IN_CHAL" - ); + require(currentChallenge(stakerAddress) == NO_CHAL_INDEX, "IN_CHAL"); } - function withdrawStakerFunds(address payable destination) - external - virtual - returns (uint256); + function withdrawStakerFunds(address payable destination) external virtual returns (uint256); } contract RollupUserLogic is AbsRollupUserLogic, IRollupUser { @@ -664,12 +587,7 @@ contract RollupUserLogic is AbsRollupUserLogic, IRollupUser { * @notice Increase the amount staked eth for the given staker * @param stakerAddress Address of the staker whose stake is increased */ - function addToDeposit(address stakerAddress) - external - payable - onlyValidator - whenNotPaused - { + function addToDeposit(address stakerAddress) external payable onlyValidator whenNotPaused { _addToDeposit(stakerAddress, msg.value); } @@ -705,7 +623,11 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { * @param nodeNum Number of the node your stake will be place one * @param nodeHash Node hash of the node with the given nodeNum */ - function newStakeOnExistingNode(uint256 tokenAmount, uint64 nodeNum, bytes32 nodeHash) external override { + function newStakeOnExistingNode( + uint256 tokenAmount, + uint64 nodeNum, + bytes32 nodeHash + ) external override { _newStake(tokenAmount); stakeOnExistingNode(nodeNum, nodeHash); /// @dev This is an external call, safe because it's at the end of the function @@ -759,20 +681,13 @@ contract ERC20RollupUserLogic is AbsRollupUserLogic, IRollupUserERC20 { { uint256 amount = withdrawFunds(msg.sender); // This is safe because it occurs after all checks and effects - require( - IERC20Upgradeable(stakeToken).transfer(destination, amount), - "TRANSFER_FAILED" - ); + require(IERC20Upgradeable(stakeToken).transfer(destination, amount), "TRANSFER_FAILED"); return amount; } function receiveTokens(uint256 tokenAmount) private { require( - IERC20Upgradeable(stakeToken).transferFrom( - msg.sender, - address(this), - tokenAmount - ), + IERC20Upgradeable(stakeToken).transferFrom(msg.sender, address(this), tokenAmount), "TRANSFER_FAIL" ); } diff --git a/solgen/src/rollup/ValidatorUtils.sol b/solgen/src/rollup/ValidatorUtils.sol index 04abde5670..ed59c19480 100644 --- a/solgen/src/rollup/ValidatorUtils.sol +++ b/solgen/src/rollup/ValidatorUtils.sol @@ -53,11 +53,7 @@ contract ValidatorUtils { address staker1, address staker2, uint256 maxDepth - ) - external - view - returns (NodeConflict memory) - { + ) external view returns (NodeConflict memory) { uint64 staker1NodeNum = rollup.latestStakedNode(staker1); uint64 staker2NodeNum = rollup.latestStakedNode(staker2); return findNodeConflict(rollup, staker1NodeNum, staker2NodeNum, maxDepth); @@ -86,7 +82,8 @@ contract ValidatorUtils { // Verify that no staker is staked on this node require( - node.stakerCount == IRollupUser(address(rollup)).countStakedZombies(firstUnresolvedNode), + node.stakerCount == + IRollupUser(address(rollup)).countStakedZombies(firstUnresolvedNode), "HAS_STAKERS" ); } @@ -123,9 +120,7 @@ contract ValidatorUtils { for (uint64 i = 0; i < stakerCount; i++) { address staker = rollup.getStakerAddress(i); uint256 latestStakedNode = rollup.latestStakedNode(staker); - if ( - latestStakedNode <= latestConfirmed && rollup.currentChallenge(staker) == 0 - ) { + if (latestStakedNode <= latestConfirmed && rollup.currentChallenge(staker) == 0) { stakers[index] = staker; index++; } @@ -136,7 +131,11 @@ contract ValidatorUtils { return stakers; } - function latestStaked(IRollupCore rollup, address staker) external view returns (uint64, Node memory) { + function latestStaked(IRollupCore rollup, address staker) + external + view + returns (uint64, Node memory) + { uint64 num = rollup.latestStakedNode(staker); if (num == 0) { num = rollup.latestConfirmed(); @@ -145,7 +144,11 @@ contract ValidatorUtils { return (num, node); } - function stakedNodes(IRollupCore rollup, address staker) external view returns (uint64[] memory) { + function stakedNodes(IRollupCore rollup, address staker) + external + view + returns (uint64[] memory) + { uint64[] memory nodes = new uint64[](100000); uint256 index = 0; for (uint64 i = rollup.latestConfirmed(); i <= rollup.latestNodeCreated(); i++) { @@ -166,11 +169,7 @@ contract ValidatorUtils { uint64 node1, uint64 node2, uint256 maxDepth - ) - public - view - returns (NodeConflict memory) - { + ) public view returns (NodeConflict memory) { uint64 firstUnresolvedNode = rollup.firstUnresolvedNode(); uint64 node1Prev = rollup.getNode(node1).prevNum; uint64 node2Prev = rollup.getNode(node2).prevNum; @@ -226,9 +225,11 @@ contract ValidatorUtils { for (uint256 i = 0; i < stakers.length; i++) { address staker = stakers[i]; uint64 challengeIndex = rollup.currentChallenge(staker); - if (challengeIndex != NO_CHAL_INDEX && + if ( + challengeIndex != NO_CHAL_INDEX && challengeManager.isTimedOut(challengeIndex) && - challengeManager.currentResponder(challengeIndex) == staker) { + challengeManager.currentResponder(challengeIndex) == staker + ) { challenges[index++] = challengeIndex; } } diff --git a/solgen/src/rollup/ValidatorWallet.sol b/solgen/src/rollup/ValidatorWallet.sol index 7768e36e96..8b13ad02d3 100644 --- a/solgen/src/rollup/ValidatorWallet.sol +++ b/solgen/src/rollup/ValidatorWallet.sol @@ -39,7 +39,7 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { uint256 numTxes = data.length; for (uint256 i = 0; i < numTxes; i++) { if (data[i].length > 0) require(destination[i].isContract(), "NO_CODE_AT_ADDR"); - (bool success, ) = address(destination[i]).call{ value: amount[i] }(data[i]); + (bool success, ) = address(destination[i]).call{value: amount[i]}(data[i]); if (!success) { assembly { let ptr := mload(0x40) @@ -57,7 +57,7 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { uint256 amount ) external payable onlyOwner { if (data.length > 0) require(destination.isContract(), "NO_CODE_AT_ADDR"); - (bool success, ) = destination.call{ value: amount }(data); + (bool success, ) = destination.call{value: amount}(data); if (!success) { assembly { let ptr := mload(0x40) @@ -84,7 +84,10 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { } } - function timeoutChallenges(IChallengeManager manager, uint64[] calldata challenges) external onlyOwner { + function timeoutChallenges(IChallengeManager manager, uint64[] calldata challenges) + external + onlyOwner + { uint256 challengesCount = challenges.length; for (uint256 i = 0; i < challengesCount; i++) { try manager.timeout(challenges[i]) {} catch (bytes memory error) { diff --git a/solgen/src/state/Deserialize.sol b/solgen/src/state/Deserialize.sol index 99f404f6a7..41df0fcd99 100644 --- a/solgen/src/state/Deserialize.sol +++ b/solgen/src/state/Deserialize.sol @@ -13,259 +13,306 @@ import "./Module.sol"; import "./GlobalState.sol"; library Deserialize { - function u8(bytes calldata proof, uint256 startOffset) internal pure returns (uint8 ret, uint256 offset) { - offset = startOffset; - ret = uint8(proof[offset]); - offset++; - } + function u8(bytes calldata proof, uint256 startOffset) + internal + pure + returns (uint8 ret, uint256 offset) + { + offset = startOffset; + ret = uint8(proof[offset]); + offset++; + } - function u16(bytes calldata proof, uint256 startOffset) internal pure returns (uint16 ret, uint256 offset) { - offset = startOffset; - for (uint256 i = 0; i < 16/8; i++) { - ret <<= 8; - ret |= uint8(proof[offset]); - offset++; - } - } + function u16(bytes calldata proof, uint256 startOffset) + internal + pure + returns (uint16 ret, uint256 offset) + { + offset = startOffset; + for (uint256 i = 0; i < 16 / 8; i++) { + ret <<= 8; + ret |= uint8(proof[offset]); + offset++; + } + } - function u32(bytes calldata proof, uint256 startOffset) internal pure returns (uint32 ret, uint256 offset) { - offset = startOffset; - for (uint256 i = 0; i < 32/8; i++) { - ret <<= 8; - ret |= uint8(proof[offset]); - offset++; - } - } + function u32(bytes calldata proof, uint256 startOffset) + internal + pure + returns (uint32 ret, uint256 offset) + { + offset = startOffset; + for (uint256 i = 0; i < 32 / 8; i++) { + ret <<= 8; + ret |= uint8(proof[offset]); + offset++; + } + } - function u64(bytes calldata proof, uint256 startOffset) internal pure returns (uint64 ret, uint256 offset) { - offset = startOffset; - for (uint256 i = 0; i < 64/8; i++) { - ret <<= 8; - ret |= uint8(proof[offset]); - offset++; - } - } + function u64(bytes calldata proof, uint256 startOffset) + internal + pure + returns (uint64 ret, uint256 offset) + { + offset = startOffset; + for (uint256 i = 0; i < 64 / 8; i++) { + ret <<= 8; + ret |= uint8(proof[offset]); + offset++; + } + } - function u256(bytes calldata proof, uint256 startOffset) internal pure returns (uint256 ret, uint256 offset) { - offset = startOffset; - for (uint256 i = 0; i < 256/8; i++) { - ret <<= 8; - ret |= uint8(proof[offset]); - offset++; - } - } + function u256(bytes calldata proof, uint256 startOffset) + internal + pure + returns (uint256 ret, uint256 offset) + { + offset = startOffset; + for (uint256 i = 0; i < 256 / 8; i++) { + ret <<= 8; + ret |= uint8(proof[offset]); + offset++; + } + } - function b32(bytes calldata proof, uint256 startOffset) internal pure returns (bytes32 ret, uint256 offset) { - offset = startOffset; - uint256 retInt; - (retInt, offset) = u256(proof, offset); - ret = bytes32(retInt); - } + function b32(bytes calldata proof, uint256 startOffset) + internal + pure + returns (bytes32 ret, uint256 offset) + { + offset = startOffset; + uint256 retInt; + (retInt, offset) = u256(proof, offset); + ret = bytes32(retInt); + } - function value(bytes calldata proof, uint256 startOffset) internal pure returns (Value memory val, uint256 offset) { - offset = startOffset; - uint8 typeInt = uint8(proof[offset]); - offset++; - require(typeInt <= uint8(ValueLib.maxValueType()), "BAD_VALUE_TYPE"); - uint256 contents; - (contents, offset) = u256(proof, offset); - val = Value({ - valueType: ValueType(typeInt), - contents: contents - }); - } + function value(bytes calldata proof, uint256 startOffset) + internal + pure + returns (Value memory val, uint256 offset) + { + offset = startOffset; + uint8 typeInt = uint8(proof[offset]); + offset++; + require(typeInt <= uint8(ValueLib.maxValueType()), "BAD_VALUE_TYPE"); + uint256 contents; + (contents, offset) = u256(proof, offset); + val = Value({valueType: ValueType(typeInt), contents: contents}); + } - function valueStack(bytes calldata proof, uint256 startOffset) internal pure returns (ValueStack memory stack, uint256 offset) { - offset = startOffset; - bytes32 remainingHash; - (remainingHash, offset) = b32(proof, offset); - uint256 provedLength; - (provedLength, offset) = u256(proof, offset); - Value[] memory proved = new Value[](provedLength); - for (uint256 i = 0; i < proved.length; i++) { - (proved[i], offset) = value(proof, offset); - } - stack = ValueStack({ - proved: ValueArray(proved), - remainingHash: remainingHash - }); - } + function valueStack(bytes calldata proof, uint256 startOffset) + internal + pure + returns (ValueStack memory stack, uint256 offset) + { + offset = startOffset; + bytes32 remainingHash; + (remainingHash, offset) = b32(proof, offset); + uint256 provedLength; + (provedLength, offset) = u256(proof, offset); + Value[] memory proved = new Value[](provedLength); + for (uint256 i = 0; i < proved.length; i++) { + (proved[i], offset) = value(proof, offset); + } + stack = ValueStack({proved: ValueArray(proved), remainingHash: remainingHash}); + } - function pcStack(bytes calldata proof, uint256 startOffset) internal pure returns (PcStack memory stack, uint256 offset) { - offset = startOffset; - bytes32 remainingHash; - (remainingHash, offset) = b32(proof, offset); - uint256 provedLength; - (provedLength, offset) = u256(proof, offset); - uint32[] memory proved = new uint32[](provedLength); - for (uint256 i = 0; i < proved.length; i++) { - (proved[i], offset) = u32(proof, offset); - } - stack = PcStack({ - proved: PcArray(proved), - remainingHash: remainingHash - }); - } + function pcStack(bytes calldata proof, uint256 startOffset) + internal + pure + returns (PcStack memory stack, uint256 offset) + { + offset = startOffset; + bytes32 remainingHash; + (remainingHash, offset) = b32(proof, offset); + uint256 provedLength; + (provedLength, offset) = u256(proof, offset); + uint32[] memory proved = new uint32[](provedLength); + for (uint256 i = 0; i < proved.length; i++) { + (proved[i], offset) = u32(proof, offset); + } + stack = PcStack({proved: PcArray(proved), remainingHash: remainingHash}); + } - function instruction(bytes calldata proof, uint256 startOffset) internal pure returns (Instruction memory inst, uint256 offset) { - offset = startOffset; - uint16 opcode; - uint256 data; - (opcode, offset) = u16(proof, offset); - (data, offset) = u256(proof, offset); - inst = Instruction({ - opcode: opcode, - argumentData: data - }); - } + function instruction(bytes calldata proof, uint256 startOffset) + internal + pure + returns (Instruction memory inst, uint256 offset) + { + offset = startOffset; + uint16 opcode; + uint256 data; + (opcode, offset) = u16(proof, offset); + (data, offset) = u256(proof, offset); + inst = Instruction({opcode: opcode, argumentData: data}); + } - function stackFrame(bytes calldata proof, uint256 startOffset) internal pure returns (StackFrame memory window, uint256 offset) { - offset = startOffset; - Value memory returnPc; - bytes32 localsMerkleRoot; - uint32 callerModule; - uint32 callerModuleInternals; - (returnPc, offset) = value(proof, offset); - (localsMerkleRoot, offset) = b32(proof, offset); - (callerModule, offset) = u32(proof, offset); - (callerModuleInternals, offset) = u32(proof, offset); - window = StackFrame({ - returnPc: returnPc, - localsMerkleRoot: localsMerkleRoot, - callerModule: callerModule, - callerModuleInternals: callerModuleInternals - }); - } + function stackFrame(bytes calldata proof, uint256 startOffset) + internal + pure + returns (StackFrame memory window, uint256 offset) + { + offset = startOffset; + Value memory returnPc; + bytes32 localsMerkleRoot; + uint32 callerModule; + uint32 callerModuleInternals; + (returnPc, offset) = value(proof, offset); + (localsMerkleRoot, offset) = b32(proof, offset); + (callerModule, offset) = u32(proof, offset); + (callerModuleInternals, offset) = u32(proof, offset); + window = StackFrame({ + returnPc: returnPc, + localsMerkleRoot: localsMerkleRoot, + callerModule: callerModule, + callerModuleInternals: callerModuleInternals + }); + } - function stackFrameWindow(bytes calldata proof, uint256 startOffset) internal pure returns (StackFrameWindow memory window, uint256 offset) { - offset = startOffset; - bytes32 remainingHash; - (remainingHash, offset) = b32(proof, offset); - StackFrame[] memory proved; - if (proof[offset] != 0) { - offset++; - proved = new StackFrame[](1); - (proved[0], offset) = stackFrame(proof, offset); - } else { - offset++; - proved = new StackFrame[](0); - } - window = StackFrameWindow({ - proved: proved, - remainingHash: remainingHash - }); - } + function stackFrameWindow(bytes calldata proof, uint256 startOffset) + internal + pure + returns (StackFrameWindow memory window, uint256 offset) + { + offset = startOffset; + bytes32 remainingHash; + (remainingHash, offset) = b32(proof, offset); + StackFrame[] memory proved; + if (proof[offset] != 0) { + offset++; + proved = new StackFrame[](1); + (proved[0], offset) = stackFrame(proof, offset); + } else { + offset++; + proved = new StackFrame[](0); + } + window = StackFrameWindow({proved: proved, remainingHash: remainingHash}); + } - function moduleMemory(bytes calldata proof, uint256 startOffset) internal pure returns (ModuleMemory memory mem, uint256 offset) { - offset = startOffset; - uint64 size; - bytes32 root; - (size, offset) = u64(proof, offset); - (root, offset) = b32(proof, offset); - mem = ModuleMemory({ - size: size, - merkleRoot: root - }); - } + function moduleMemory(bytes calldata proof, uint256 startOffset) + internal + pure + returns (ModuleMemory memory mem, uint256 offset) + { + offset = startOffset; + uint64 size; + bytes32 root; + (size, offset) = u64(proof, offset); + (root, offset) = b32(proof, offset); + mem = ModuleMemory({size: size, merkleRoot: root}); + } - function module(bytes calldata proof, uint256 startOffset) internal pure returns (Module memory mod, uint256 offset) { - offset = startOffset; - bytes32 globalsMerkleRoot; - ModuleMemory memory mem; - bytes32 tablesMerkleRoot; - bytes32 functionsMerkleRoot; - uint32 internalsOffset; - (globalsMerkleRoot, offset) = b32(proof, offset); - (mem, offset) = moduleMemory(proof, offset); - (tablesMerkleRoot, offset) = b32(proof, offset); - (functionsMerkleRoot, offset) = b32(proof, offset); - (internalsOffset, offset) = u32(proof, offset); - mod = Module({ - globalsMerkleRoot: globalsMerkleRoot, - moduleMemory: mem, - tablesMerkleRoot: tablesMerkleRoot, - functionsMerkleRoot: functionsMerkleRoot, - internalsOffset: internalsOffset - }); - } + function module(bytes calldata proof, uint256 startOffset) + internal + pure + returns (Module memory mod, uint256 offset) + { + offset = startOffset; + bytes32 globalsMerkleRoot; + ModuleMemory memory mem; + bytes32 tablesMerkleRoot; + bytes32 functionsMerkleRoot; + uint32 internalsOffset; + (globalsMerkleRoot, offset) = b32(proof, offset); + (mem, offset) = moduleMemory(proof, offset); + (tablesMerkleRoot, offset) = b32(proof, offset); + (functionsMerkleRoot, offset) = b32(proof, offset); + (internalsOffset, offset) = u32(proof, offset); + mod = Module({ + globalsMerkleRoot: globalsMerkleRoot, + moduleMemory: mem, + tablesMerkleRoot: tablesMerkleRoot, + functionsMerkleRoot: functionsMerkleRoot, + internalsOffset: internalsOffset + }); + } - function globalState(bytes calldata proof, uint256 startOffset) internal pure returns (GlobalState memory state, uint256 offset) { - offset = startOffset; + function globalState(bytes calldata proof, uint256 startOffset) + internal + pure + returns (GlobalState memory state, uint256 offset) + { + offset = startOffset; - // using constant ints for array size requires newer solidity - bytes32[2] memory bytes32_vals; - uint64[2] memory u64_vals; + // using constant ints for array size requires newer solidity + bytes32[2] memory bytes32_vals; + uint64[2] memory u64_vals; - for (uint8 i = 0; i< GlobalStateLib.BYTES32_VALS_NUM; i++) { - (bytes32_vals[i], offset) = b32(proof, offset); - } - for (uint8 i = 0; i< GlobalStateLib.U64_VALS_NUM; i++) { - (u64_vals[i], offset) = u64(proof, offset); - } - state = GlobalState({ - bytes32_vals: bytes32_vals, - u64_vals: u64_vals - }); - } + for (uint8 i = 0; i < GlobalStateLib.BYTES32_VALS_NUM; i++) { + (bytes32_vals[i], offset) = b32(proof, offset); + } + for (uint8 i = 0; i < GlobalStateLib.U64_VALS_NUM; i++) { + (u64_vals[i], offset) = u64(proof, offset); + } + state = GlobalState({bytes32_vals: bytes32_vals, u64_vals: u64_vals}); + } - function machine(bytes calldata proof, uint256 startOffset) internal pure returns (Machine memory mach, uint256 offset) { - offset = startOffset; - MachineStatus status; - { - uint8 status_u8; - (status_u8, offset) = u8(proof, offset); - if (status_u8 == 0) { - status = MachineStatus.RUNNING; - } else if (status_u8 == 1) { - status = MachineStatus.FINISHED; - } else if (status_u8 == 2) { - status = MachineStatus.ERRORED; - } else if (status_u8 == 3) { - status = MachineStatus.TOO_FAR; - } else { - revert("UNKNOWN_MACH_STATUS"); - } - } - ValueStack memory values; - ValueStack memory internalStack; - PcStack memory blocks; - bytes32 globalStateHash; - uint32 moduleIdx; - uint32 functionIdx; - uint32 functionPc; - StackFrameWindow memory frameStack; - bytes32 modulesRoot; - (values, offset) = valueStack(proof, offset); - (internalStack, offset) = valueStack(proof, offset); - (blocks, offset) = pcStack(proof, offset); - (frameStack, offset) = stackFrameWindow(proof, offset); - (globalStateHash, offset) = b32(proof, offset); - (moduleIdx, offset) = u32(proof, offset); - (functionIdx, offset) = u32(proof, offset); - (functionPc, offset) = u32(proof, offset); - (modulesRoot, offset) = b32(proof, offset); - mach = Machine({ - status: status, - valueStack: values, - internalStack: internalStack, - blockStack: blocks, - frameStack: frameStack, - globalStateHash: globalStateHash, - moduleIdx: moduleIdx, - functionIdx: functionIdx, - functionPc: functionPc, - modulesRoot: modulesRoot - }); - } + function machine(bytes calldata proof, uint256 startOffset) + internal + pure + returns (Machine memory mach, uint256 offset) + { + offset = startOffset; + MachineStatus status; + { + uint8 status_u8; + (status_u8, offset) = u8(proof, offset); + if (status_u8 == 0) { + status = MachineStatus.RUNNING; + } else if (status_u8 == 1) { + status = MachineStatus.FINISHED; + } else if (status_u8 == 2) { + status = MachineStatus.ERRORED; + } else if (status_u8 == 3) { + status = MachineStatus.TOO_FAR; + } else { + revert("UNKNOWN_MACH_STATUS"); + } + } + ValueStack memory values; + ValueStack memory internalStack; + PcStack memory blocks; + bytes32 globalStateHash; + uint32 moduleIdx; + uint32 functionIdx; + uint32 functionPc; + StackFrameWindow memory frameStack; + bytes32 modulesRoot; + (values, offset) = valueStack(proof, offset); + (internalStack, offset) = valueStack(proof, offset); + (blocks, offset) = pcStack(proof, offset); + (frameStack, offset) = stackFrameWindow(proof, offset); + (globalStateHash, offset) = b32(proof, offset); + (moduleIdx, offset) = u32(proof, offset); + (functionIdx, offset) = u32(proof, offset); + (functionPc, offset) = u32(proof, offset); + (modulesRoot, offset) = b32(proof, offset); + mach = Machine({ + status: status, + valueStack: values, + internalStack: internalStack, + blockStack: blocks, + frameStack: frameStack, + globalStateHash: globalStateHash, + moduleIdx: moduleIdx, + functionIdx: functionIdx, + functionPc: functionPc, + modulesRoot: modulesRoot + }); + } - function merkleProof(bytes calldata proof, uint256 startOffset) internal pure returns (MerkleProof memory merkle, uint256 offset) { - offset = startOffset; - uint8 length; - (length, offset) = u8(proof, offset); - bytes32[] memory counterparts = new bytes32[](length); - for (uint8 i = 0; i < length; i++) { - (counterparts[i], offset) = b32(proof, offset); - } - merkle = MerkleProof(counterparts); - } + function merkleProof(bytes calldata proof, uint256 startOffset) + internal + pure + returns (MerkleProof memory merkle, uint256 offset) + { + offset = startOffset; + uint8 length; + (length, offset) = u8(proof, offset); + bytes32[] memory counterparts = new bytes32[](length); + for (uint8 i = 0; i < length; i++) { + (counterparts[i], offset) = b32(proof, offset); + } + merkle = MerkleProof(counterparts); + } } diff --git a/solgen/src/state/GlobalState.sol b/solgen/src/state/GlobalState.sol index cc2e8b5107..ae0db6277e 100644 --- a/solgen/src/state/GlobalState.sol +++ b/solgen/src/state/GlobalState.sol @@ -2,37 +2,40 @@ pragma solidity ^0.8.0; struct GlobalState { - bytes32[2] bytes32_vals; - uint64[2] u64_vals; + bytes32[2] bytes32_vals; + uint64[2] u64_vals; } - library GlobalStateLib { - uint16 constant BYTES32_VALS_NUM = 2; - uint16 constant U64_VALS_NUM = 2; - function hash(GlobalState memory state) internal pure returns (bytes32) { - return keccak256(abi.encodePacked( - "Global state:", - state.bytes32_vals[0], - state.bytes32_vals[1], - state.u64_vals[0], - state.u64_vals[1] - )); - } + uint16 constant BYTES32_VALS_NUM = 2; + uint16 constant U64_VALS_NUM = 2; + + function hash(GlobalState memory state) internal pure returns (bytes32) { + return + keccak256( + abi.encodePacked( + "Global state:", + state.bytes32_vals[0], + state.bytes32_vals[1], + state.u64_vals[0], + state.u64_vals[1] + ) + ); + } - function getBlockHash(GlobalState memory state) internal pure returns (bytes32) { - return state.bytes32_vals[0]; - } + function getBlockHash(GlobalState memory state) internal pure returns (bytes32) { + return state.bytes32_vals[0]; + } - function getSendRoot(GlobalState memory state) internal pure returns (bytes32) { - return state.bytes32_vals[1]; - } + function getSendRoot(GlobalState memory state) internal pure returns (bytes32) { + return state.bytes32_vals[1]; + } - function getInboxPosition(GlobalState memory state) internal pure returns (uint64) { - return state.u64_vals[0]; - } + function getInboxPosition(GlobalState memory state) internal pure returns (uint64) { + return state.u64_vals[0]; + } - function getPositionInMessage(GlobalState memory state) internal pure returns (uint64) { - return state.u64_vals[1]; - } + function getPositionInMessage(GlobalState memory state) internal pure returns (uint64) { + return state.u64_vals[1]; + } } diff --git a/solgen/src/state/Instructions.sol b/solgen/src/state/Instructions.sol index 8eb89a60e4..8339a55ea0 100644 --- a/solgen/src/state/Instructions.sol +++ b/solgen/src/state/Instructions.sol @@ -151,10 +151,7 @@ library Instructions { uint256 constant INBOX_INDEX_DELAYED = 1; function hash(Instruction memory inst) internal pure returns (bytes32) { - return - keccak256( - abi.encodePacked("Instruction:", inst.opcode, inst.argumentData) - ); + return keccak256(abi.encodePacked("Instruction:", inst.opcode, inst.argumentData)); } function newNop() internal pure returns (Instruction memory) { diff --git a/solgen/src/state/Machine.sol b/solgen/src/state/Machine.sol index fc60b0df2d..95f5a1a544 100644 --- a/solgen/src/state/Machine.sol +++ b/solgen/src/state/Machine.sol @@ -7,56 +7,56 @@ import "./Instructions.sol"; import "./StackFrame.sol"; enum MachineStatus { - RUNNING, - FINISHED, - ERRORED, - TOO_FAR + RUNNING, + FINISHED, + ERRORED, + TOO_FAR } struct Machine { - MachineStatus status; - ValueStack valueStack; - ValueStack internalStack; - PcStack blockStack; - StackFrameWindow frameStack; - bytes32 globalStateHash; - uint32 moduleIdx; - uint32 functionIdx; - uint32 functionPc; - bytes32 modulesRoot; + MachineStatus status; + ValueStack valueStack; + ValueStack internalStack; + PcStack blockStack; + StackFrameWindow frameStack; + bytes32 globalStateHash; + uint32 moduleIdx; + uint32 functionIdx; + uint32 functionPc; + bytes32 modulesRoot; } library MachineLib { - using PcStackLib for PcStack; - using StackFrameLib for StackFrameWindow; - using ValueStackLib for ValueStack; + using PcStackLib for PcStack; + using StackFrameLib for StackFrameWindow; + using ValueStackLib for ValueStack; - function hash(Machine memory mach) internal pure returns (bytes32) { - // Warning: the non-running hashes are replicated in Challenge - if (mach.status == MachineStatus.RUNNING) { - return keccak256(abi.encodePacked( - "Machine running:", - mach.valueStack.hash(), - mach.internalStack.hash(), - mach.blockStack.hash(), - mach.frameStack.hash(), - mach.globalStateHash, - mach.moduleIdx, - mach.functionIdx, - mach.functionPc, - mach.modulesRoot - )); - } else if (mach.status == MachineStatus.FINISHED) { - return keccak256(abi.encodePacked( - "Machine finished:", - mach.globalStateHash - )); - } else if (mach.status == MachineStatus.ERRORED) { - return keccak256(abi.encodePacked("Machine errored:")); - } else if (mach.status == MachineStatus.TOO_FAR) { - return keccak256(abi.encodePacked("Machine too far:")); - } else { - revert("BAD_MACH_STATUS"); - } - } + function hash(Machine memory mach) internal pure returns (bytes32) { + // Warning: the non-running hashes are replicated in Challenge + if (mach.status == MachineStatus.RUNNING) { + return + keccak256( + abi.encodePacked( + "Machine running:", + mach.valueStack.hash(), + mach.internalStack.hash(), + mach.blockStack.hash(), + mach.frameStack.hash(), + mach.globalStateHash, + mach.moduleIdx, + mach.functionIdx, + mach.functionPc, + mach.modulesRoot + ) + ); + } else if (mach.status == MachineStatus.FINISHED) { + return keccak256(abi.encodePacked("Machine finished:", mach.globalStateHash)); + } else if (mach.status == MachineStatus.ERRORED) { + return keccak256(abi.encodePacked("Machine errored:")); + } else if (mach.status == MachineStatus.TOO_FAR) { + return keccak256(abi.encodePacked("Machine too far:")); + } else { + revert("BAD_MACH_STATUS"); + } + } } diff --git a/solgen/src/state/MerkleProof.sol b/solgen/src/state/MerkleProof.sol index 8b0f0755fe..a112e7002b 100644 --- a/solgen/src/state/MerkleProof.sol +++ b/solgen/src/state/MerkleProof.sol @@ -6,55 +6,91 @@ import "./Instructions.sol"; import "./Module.sol"; struct MerkleProof { - bytes32[] counterparts; + bytes32[] counterparts; } library MerkleProofLib { - using ModuleLib for Module; - using ValueLib for Value; - - function computeRootFromValue(MerkleProof memory proof, uint256 index, Value memory leaf) internal pure returns (bytes32) { - return computeRootUnsafe(proof, index, leaf.hash(), "Value merkle tree:"); - } - - function computeRootFromInstruction(MerkleProof memory proof, uint256 index, Instruction memory inst) internal pure returns (bytes32) { - return computeRootUnsafe(proof, index, Instructions.hash(inst), "Instruction merkle tree:"); - } - - function computeRootFromFunction(MerkleProof memory proof, uint256 index, bytes32 codeRoot) internal pure returns (bytes32) { - bytes32 h = keccak256(abi.encodePacked("Function:", codeRoot)); - return computeRootUnsafe(proof, index, h, "Function merkle tree:"); - } - - function computeRootFromMemory(MerkleProof memory proof, uint256 index, bytes32 contents) internal pure returns (bytes32) { - bytes32 h = keccak256(abi.encodePacked("Memory leaf:", contents)); - return computeRootUnsafe(proof, index, h, "Memory merkle tree:"); - } - - function computeRootFromElement(MerkleProof memory proof, uint256 index, bytes32 funcTypeHash, Value memory val) internal pure returns (bytes32) { - bytes32 h = keccak256(abi.encodePacked("Table element:", funcTypeHash, val.hash())); - return computeRootUnsafe(proof, index, h, "Table element merkle tree:"); - } - - function computeRootFromTable(MerkleProof memory proof, uint256 index, uint8 tableType, uint64 tableSize, bytes32 elementsRoot) internal pure returns (bytes32) { - bytes32 h = keccak256(abi.encodePacked("Table:", tableType, tableSize, elementsRoot)); - return computeRootUnsafe(proof, index, h, "Table merkle tree:"); - } - - function computeRootFromModule(MerkleProof memory proof, uint256 index, Module memory mod) internal pure returns (bytes32) { - return computeRootUnsafe(proof, index, mod.hash(), "Module merkle tree:"); - } - - // WARNING: leafHash must be computed in such a way that it cannot be a non-leaf hash. - function computeRootUnsafe(MerkleProof memory proof, uint256 index, bytes32 leafHash, string memory prefix) internal pure returns (bytes32 h) { - h = leafHash; - for (uint256 layer = 0; layer < proof.counterparts.length; layer++) { - if (index & 1 == 0) { - h = keccak256(abi.encodePacked(prefix, h, proof.counterparts[layer])); - } else { - h = keccak256(abi.encodePacked(prefix, proof.counterparts[layer], h)); - } - index >>= 1; - } - } + using ModuleLib for Module; + using ValueLib for Value; + + function computeRootFromValue( + MerkleProof memory proof, + uint256 index, + Value memory leaf + ) internal pure returns (bytes32) { + return computeRootUnsafe(proof, index, leaf.hash(), "Value merkle tree:"); + } + + function computeRootFromInstruction( + MerkleProof memory proof, + uint256 index, + Instruction memory inst + ) internal pure returns (bytes32) { + return computeRootUnsafe(proof, index, Instructions.hash(inst), "Instruction merkle tree:"); + } + + function computeRootFromFunction( + MerkleProof memory proof, + uint256 index, + bytes32 codeRoot + ) internal pure returns (bytes32) { + bytes32 h = keccak256(abi.encodePacked("Function:", codeRoot)); + return computeRootUnsafe(proof, index, h, "Function merkle tree:"); + } + + function computeRootFromMemory( + MerkleProof memory proof, + uint256 index, + bytes32 contents + ) internal pure returns (bytes32) { + bytes32 h = keccak256(abi.encodePacked("Memory leaf:", contents)); + return computeRootUnsafe(proof, index, h, "Memory merkle tree:"); + } + + function computeRootFromElement( + MerkleProof memory proof, + uint256 index, + bytes32 funcTypeHash, + Value memory val + ) internal pure returns (bytes32) { + bytes32 h = keccak256(abi.encodePacked("Table element:", funcTypeHash, val.hash())); + return computeRootUnsafe(proof, index, h, "Table element merkle tree:"); + } + + function computeRootFromTable( + MerkleProof memory proof, + uint256 index, + uint8 tableType, + uint64 tableSize, + bytes32 elementsRoot + ) internal pure returns (bytes32) { + bytes32 h = keccak256(abi.encodePacked("Table:", tableType, tableSize, elementsRoot)); + return computeRootUnsafe(proof, index, h, "Table merkle tree:"); + } + + function computeRootFromModule( + MerkleProof memory proof, + uint256 index, + Module memory mod + ) internal pure returns (bytes32) { + return computeRootUnsafe(proof, index, mod.hash(), "Module merkle tree:"); + } + + // WARNING: leafHash must be computed in such a way that it cannot be a non-leaf hash. + function computeRootUnsafe( + MerkleProof memory proof, + uint256 index, + bytes32 leafHash, + string memory prefix + ) internal pure returns (bytes32 h) { + h = leafHash; + for (uint256 layer = 0; layer < proof.counterparts.length; layer++) { + if (index & 1 == 0) { + h = keccak256(abi.encodePacked(prefix, h, proof.counterparts[layer])); + } else { + h = keccak256(abi.encodePacked(prefix, proof.counterparts[layer], h)); + } + index >>= 1; + } + } } diff --git a/solgen/src/state/ModuleMemory.sol b/solgen/src/state/ModuleMemory.sol index b9e089f933..49ee905bb4 100644 --- a/solgen/src/state/ModuleMemory.sol +++ b/solgen/src/state/ModuleMemory.sol @@ -5,22 +5,35 @@ import "./MerkleProof.sol"; import "./Deserialize.sol"; struct ModuleMemory { - uint64 size; - bytes32 merkleRoot; + uint64 size; + bytes32 merkleRoot; } library ModuleMemoryLib { - using MerkleProofLib for MerkleProof; + using MerkleProofLib for MerkleProof; - function hash(ModuleMemory memory mem) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("Memory:", mem.size, mem.merkleRoot)); - } + function hash(ModuleMemory memory mem) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("Memory:", mem.size, mem.merkleRoot)); + } - function proveLeaf(ModuleMemory memory mem, uint256 leafIdx, bytes calldata proof, uint256 startOffset) internal pure returns (bytes32 contents, uint256 offset, MerkleProof memory merkle) { - offset = startOffset; - (contents, offset) = Deserialize.b32(proof, offset); - (merkle, offset) = Deserialize.merkleProof(proof, offset); - bytes32 recomputedRoot = merkle.computeRootFromMemory(leafIdx, contents); - require(recomputedRoot == mem.merkleRoot, "WRONG_MEM_ROOT"); - } + function proveLeaf( + ModuleMemory memory mem, + uint256 leafIdx, + bytes calldata proof, + uint256 startOffset + ) + internal + pure + returns ( + bytes32 contents, + uint256 offset, + MerkleProof memory merkle + ) + { + offset = startOffset; + (contents, offset) = Deserialize.b32(proof, offset); + (merkle, offset) = Deserialize.merkleProof(proof, offset); + bytes32 recomputedRoot = merkle.computeRootFromMemory(leafIdx, contents); + require(recomputedRoot == mem.merkleRoot, "WRONG_MEM_ROOT"); + } } diff --git a/solgen/src/state/PcArray.sol b/solgen/src/state/PcArray.sol index c9f579ffb9..0b70dcfdb3 100644 --- a/solgen/src/state/PcArray.sol +++ b/solgen/src/state/PcArray.sol @@ -2,37 +2,41 @@ pragma solidity ^0.8.0; struct PcArray { - uint32[] inner; + uint32[] inner; } library PcArrayLib { - function get(PcArray memory arr, uint256 index) internal pure returns (uint32) { - return arr.inner[index]; - } + function get(PcArray memory arr, uint256 index) internal pure returns (uint32) { + return arr.inner[index]; + } - function set(PcArray memory arr, uint256 index, uint32 val) internal pure { - arr.inner[index] = val; - } + function set( + PcArray memory arr, + uint256 index, + uint32 val + ) internal pure { + arr.inner[index] = val; + } - function length(PcArray memory arr) internal pure returns (uint256) { - return arr.inner.length; - } + function length(PcArray memory arr) internal pure returns (uint256) { + return arr.inner.length; + } - function push(PcArray memory arr, uint32 val) internal pure { - uint32[] memory newInner = new uint32[](arr.inner.length + 1); - for (uint256 i = 0; i < arr.inner.length; i++) { - newInner[i] = arr.inner[i]; - } - newInner[arr.inner.length] = val; - arr.inner = newInner; - } + function push(PcArray memory arr, uint32 val) internal pure { + uint32[] memory newInner = new uint32[](arr.inner.length + 1); + for (uint256 i = 0; i < arr.inner.length; i++) { + newInner[i] = arr.inner[i]; + } + newInner[arr.inner.length] = val; + arr.inner = newInner; + } - function pop(PcArray memory arr) internal pure returns (uint32 popped) { - popped = arr.inner[arr.inner.length - 1]; - uint32[] memory newInner = new uint32[](arr.inner.length - 1); - for (uint256 i = 0; i < newInner.length; i++) { - newInner[i] = arr.inner[i]; - } - arr.inner = newInner; - } + function pop(PcArray memory arr) internal pure returns (uint32 popped) { + popped = arr.inner[arr.inner.length - 1]; + uint32[] memory newInner = new uint32[](arr.inner.length - 1); + for (uint256 i = 0; i < newInner.length; i++) { + newInner[i] = arr.inner[i]; + } + arr.inner = newInner; + } } diff --git a/solgen/src/state/PcStack.sol b/solgen/src/state/PcStack.sol index ebb3dde400..25e1f401f4 100644 --- a/solgen/src/state/PcStack.sol +++ b/solgen/src/state/PcStack.sol @@ -4,26 +4,26 @@ pragma solidity ^0.8.0; import "./PcArray.sol"; struct PcStack { - PcArray proved; - bytes32 remainingHash; + PcArray proved; + bytes32 remainingHash; } library PcStackLib { - using PcArrayLib for PcArray; + using PcArrayLib for PcArray; - function hash(PcStack memory stack) internal pure returns (bytes32 h) { - h = stack.remainingHash; - uint256 len = stack.proved.length(); - for (uint256 i = 0; i < len; i++) { - h = keccak256(abi.encodePacked("Program counter stack:", stack.proved.get(i), h)); - } - } + function hash(PcStack memory stack) internal pure returns (bytes32 h) { + h = stack.remainingHash; + uint256 len = stack.proved.length(); + for (uint256 i = 0; i < len; i++) { + h = keccak256(abi.encodePacked("Program counter stack:", stack.proved.get(i), h)); + } + } - function pop(PcStack memory stack) internal pure returns (uint32) { - return stack.proved.pop(); - } + function pop(PcStack memory stack) internal pure returns (uint32) { + return stack.proved.pop(); + } - function push(PcStack memory stack, uint32 val) internal pure { - return stack.proved.push(val); - } + function push(PcStack memory stack, uint32 val) internal pure { + return stack.proved.push(val); + } } diff --git a/solgen/src/state/StackFrame.sol b/solgen/src/state/StackFrame.sol index 4a443f6b99..b39e773ff4 100644 --- a/solgen/src/state/StackFrame.sol +++ b/solgen/src/state/StackFrame.sol @@ -31,49 +31,26 @@ library StackFrameLib { ); } - function hash(StackFrameWindow memory window) - internal - pure - returns (bytes32 h) - { + function hash(StackFrameWindow memory window) internal pure returns (bytes32 h) { h = window.remainingHash; for (uint256 i = 0; i < window.proved.length; i++) { - h = keccak256( - abi.encodePacked( - "Stack frame stack:", - hash(window.proved[i]), - h - ) - ); + h = keccak256(abi.encodePacked("Stack frame stack:", hash(window.proved[i]), h)); } } - function peek(StackFrameWindow memory window) - internal - pure - returns (StackFrame memory) - { + function peek(StackFrameWindow memory window) internal pure returns (StackFrame memory) { require(window.proved.length == 1, "BAD_WINDOW_LENGTH"); return window.proved[0]; } - function pop(StackFrameWindow memory window) - internal - pure - returns (StackFrame memory frame) - { + function pop(StackFrameWindow memory window) internal pure returns (StackFrame memory frame) { require(window.proved.length == 1, "BAD_WINDOW_LENGTH"); frame = window.proved[0]; window.proved = new StackFrame[](0); } - function push(StackFrameWindow memory window, StackFrame memory frame) - internal - pure - { - StackFrame[] memory newProved = new StackFrame[]( - window.proved.length + 1 - ); + function push(StackFrameWindow memory window, StackFrame memory frame) internal pure { + StackFrame[] memory newProved = new StackFrame[](window.proved.length + 1); for (uint256 i = 0; i < window.proved.length; i++) { newProved[i] = window.proved[i]; } diff --git a/solgen/src/state/Value.sol b/solgen/src/state/Value.sol index 5ce8fb3af2..5c041d5fcb 100644 --- a/solgen/src/state/Value.sol +++ b/solgen/src/state/Value.sol @@ -2,78 +2,73 @@ pragma solidity ^0.8.0; enum ValueType { - I32, - I64, - F32, - F64, - REF_NULL, - FUNC_REF, - INTERNAL_REF, - STACK_BOUNDARY + I32, + I64, + F32, + F64, + REF_NULL, + FUNC_REF, + INTERNAL_REF, + STACK_BOUNDARY } struct Value { - ValueType valueType; - uint256 contents; + ValueType valueType; + uint256 contents; } library ValueLib { - function hash(Value memory val) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("Value:", val.valueType, val.contents)); - } + function hash(Value memory val) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("Value:", val.valueType, val.contents)); + } - function maxValueType() internal pure returns (ValueType) { - return ValueType.STACK_BOUNDARY; - } + function maxValueType() internal pure returns (ValueType) { + return ValueType.STACK_BOUNDARY; + } - function isNumeric(ValueType val) internal pure returns (bool) { - return val == ValueType.I32 || val == ValueType.I64 || val == ValueType.F32 || val == ValueType.F64; - } + function isNumeric(ValueType val) internal pure returns (bool) { + return + val == ValueType.I32 || + val == ValueType.I64 || + val == ValueType.F32 || + val == ValueType.F64; + } - function isNumeric(Value memory val) internal pure returns (bool) { - return isNumeric(val.valueType); - } + function isNumeric(Value memory val) internal pure returns (bool) { + return isNumeric(val.valueType); + } - function assumeI32(Value memory val) internal pure returns(uint32) { - uint uintval = uint(val.contents); - require(val.valueType == ValueType.I32, "NOT_I32"); - require(uintval < (1<<32), "BAD_I32"); - return uint32(uintval); - } + function assumeI32(Value memory val) internal pure returns (uint32) { + uint256 uintval = uint256(val.contents); + require(val.valueType == ValueType.I32, "NOT_I32"); + require(uintval < (1 << 32), "BAD_I32"); + return uint32(uintval); + } - function assumeI64(Value memory val) internal pure returns(uint64) { - uint uintval = uint(val.contents); - require(val.valueType == ValueType.I64, "NOT_I64"); - require(uintval < (1<<64), "BAD_I64"); - return uint64(uintval); - } + function assumeI64(Value memory val) internal pure returns (uint64) { + uint256 uintval = uint256(val.contents); + require(val.valueType == ValueType.I64, "NOT_I64"); + require(uintval < (1 << 64), "BAD_I64"); + return uint64(uintval); + } - function newRefNull() internal pure returns (Value memory) { - return Value({ - valueType: ValueType.REF_NULL, - contents: 0 - }); - } + function newRefNull() internal pure returns (Value memory) { + return Value({valueType: ValueType.REF_NULL, contents: 0}); + } - function newI32(uint32 x) internal pure returns (Value memory) { - return Value({ - valueType: ValueType.I32, - contents: uint256(x) - }); - } + function newI32(uint32 x) internal pure returns (Value memory) { + return Value({valueType: ValueType.I32, contents: uint256(x)}); + } - function newI64(uint64 x) internal pure returns (Value memory) { - return Value({ - valueType: ValueType.I64, - contents: uint256(x) - }); - } + function newI64(uint64 x) internal pure returns (Value memory) { + return Value({valueType: ValueType.I64, contents: uint256(x)}); + } - function newBoolean(bool x) internal pure returns (Value memory) { - if (x) { - return newI32(uint32(1)); - } else { - return newI32(uint32(0)); - } - } + function newBoolean(bool x) internal pure returns (Value memory) { + if (x) { + return newI32(uint32(1)); + } else { + return newI32(uint32(0)); + } + } } diff --git a/solgen/src/state/ValueArray.sol b/solgen/src/state/ValueArray.sol index 1d0b67cd43..44e6cf349b 100644 --- a/solgen/src/state/ValueArray.sol +++ b/solgen/src/state/ValueArray.sol @@ -4,37 +4,41 @@ pragma solidity ^0.8.0; import "./Value.sol"; struct ValueArray { - Value[] inner; + Value[] inner; } library ValueArrayLib { - function get(ValueArray memory arr, uint256 index) internal pure returns (Value memory) { - return arr.inner[index]; - } + function get(ValueArray memory arr, uint256 index) internal pure returns (Value memory) { + return arr.inner[index]; + } - function set(ValueArray memory arr, uint256 index, Value memory val) internal pure { - arr.inner[index] = val; - } + function set( + ValueArray memory arr, + uint256 index, + Value memory val + ) internal pure { + arr.inner[index] = val; + } - function length(ValueArray memory arr) internal pure returns (uint256) { - return arr.inner.length; - } + function length(ValueArray memory arr) internal pure returns (uint256) { + return arr.inner.length; + } - function push(ValueArray memory arr, Value memory val) internal pure { - Value[] memory newInner = new Value[](arr.inner.length + 1); - for (uint256 i = 0; i < arr.inner.length; i++) { - newInner[i] = arr.inner[i]; - } - newInner[arr.inner.length] = val; - arr.inner = newInner; - } + function push(ValueArray memory arr, Value memory val) internal pure { + Value[] memory newInner = new Value[](arr.inner.length + 1); + for (uint256 i = 0; i < arr.inner.length; i++) { + newInner[i] = arr.inner[i]; + } + newInner[arr.inner.length] = val; + arr.inner = newInner; + } - function pop(ValueArray memory arr) internal pure returns (Value memory popped) { - popped = arr.inner[arr.inner.length - 1]; - Value[] memory newInner = new Value[](arr.inner.length - 1); - for (uint256 i = 0; i < newInner.length; i++) { - newInner[i] = arr.inner[i]; - } - arr.inner = newInner; - } + function pop(ValueArray memory arr) internal pure returns (Value memory popped) { + popped = arr.inner[arr.inner.length - 1]; + Value[] memory newInner = new Value[](arr.inner.length - 1); + for (uint256 i = 0; i < newInner.length; i++) { + newInner[i] = arr.inner[i]; + } + arr.inner = newInner; + } } diff --git a/solgen/src/state/ValueStack.sol b/solgen/src/state/ValueStack.sol index e5a1787dc7..c73bbcba8d 100644 --- a/solgen/src/state/ValueStack.sol +++ b/solgen/src/state/ValueStack.sol @@ -5,40 +5,44 @@ import "./Value.sol"; import "./ValueArray.sol"; struct ValueStack { - ValueArray proved; - bytes32 remainingHash; + ValueArray proved; + bytes32 remainingHash; } library ValueStackLib { - using ValueLib for Value; - using ValueArrayLib for ValueArray; - - function hash(ValueStack memory stack) internal pure returns (bytes32 h) { - h = stack.remainingHash; - uint256 len = stack.proved.length(); - for (uint256 i = 0; i < len; i++) { - h = keccak256(abi.encodePacked("Value stack:", stack.proved.get(i).hash(), h)); - } - } - - function peek(ValueStack memory stack) internal pure returns (Value memory) { - uint256 len = stack.proved.length(); - return stack.proved.get(len - 1); - } - - function pop(ValueStack memory stack) internal pure returns (Value memory) { - return stack.proved.pop(); - } - - function push(ValueStack memory stack, Value memory val) internal pure { - return stack.proved.push(val); - } - - function isEmpty(ValueStack memory stack) internal pure returns (bool) { - return stack.proved.length() == 0 && stack.remainingHash == bytes32(0); - } - - function hasProvenDepthLessThan(ValueStack memory stack, uint256 bound) internal pure returns (bool) { - return stack.proved.length() < bound && stack.remainingHash == bytes32(0); - } + using ValueLib for Value; + using ValueArrayLib for ValueArray; + + function hash(ValueStack memory stack) internal pure returns (bytes32 h) { + h = stack.remainingHash; + uint256 len = stack.proved.length(); + for (uint256 i = 0; i < len; i++) { + h = keccak256(abi.encodePacked("Value stack:", stack.proved.get(i).hash(), h)); + } + } + + function peek(ValueStack memory stack) internal pure returns (Value memory) { + uint256 len = stack.proved.length(); + return stack.proved.get(len - 1); + } + + function pop(ValueStack memory stack) internal pure returns (Value memory) { + return stack.proved.pop(); + } + + function push(ValueStack memory stack, Value memory val) internal pure { + return stack.proved.push(val); + } + + function isEmpty(ValueStack memory stack) internal pure returns (bool) { + return stack.proved.length() == 0 && stack.remainingHash == bytes32(0); + } + + function hasProvenDepthLessThan(ValueStack memory stack, uint256 bound) + internal + pure + returns (bool) + { + return stack.proved.length() < bound && stack.remainingHash == bytes32(0); + } } From 01f0d046e2fc71ff51574d803b9e7e3f4676ae58 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Tue, 22 Feb 2022 20:58:07 -0500 Subject: [PATCH 102/110] Fix solhint issues --- solgen/.solhint.json | 9 +- solgen/src/bridge/Bridge.sol | 4 + solgen/src/bridge/Inbox.sol | 1 + solgen/src/bridge/Outbox.sol | 4 +- solgen/src/challenge/ChallengeManager.sol | 4 +- solgen/src/libraries/AddressAliasHelper.sol | 6 +- solgen/src/mocks/MockResultReceiver.sol | 2 +- solgen/src/osp/HashProofHelper.sol | 4 +- solgen/src/osp/IOneStepProofEntry.sol | 2 +- solgen/src/osp/OneStepProofEntry.sol | 8 +- solgen/src/osp/OneStepProverHostIo.sol | 12 +- solgen/src/osp/OneStepProverMath.sol | 6 +- solgen/src/osp/OneStepProverMemory.sol | 4 +- solgen/src/rollup/RollupCore.sol | 2 +- solgen/src/rollup/RollupEventBridge.sol | 4 +- solgen/src/rollup/RollupUserLogic.sol | 2 +- solgen/src/rollup/ValidatorWallet.sol | 4 + solgen/src/state/Deserialize.sol | 22 +- solgen/src/state/GlobalState.sol | 24 +- solgen/src/state/Instructions.sol | 284 ++++++++++---------- 20 files changed, 211 insertions(+), 197 deletions(-) diff --git a/solgen/.solhint.json b/solgen/.solhint.json index b889fd8ed8..47fbcedbb6 100644 --- a/solgen/.solhint.json +++ b/solgen/.solhint.json @@ -2,12 +2,17 @@ "extends": ["solhint:recommended"], "rules": { "prettier/prettier": "error", - "avoid-throw": false, + "avoid-throw": "off", "avoid-suicide": "error", "avoid-sha3": "warn", "max-line-length": "off", "compiler-version": "off", - "func-visibility": ["warn",{"ignoreConstructors":true}] + "func-visibility": ["warn",{"ignoreConstructors":true}], + "no-empty-blocks": "off", + "reason-string": ["warn",{"maxLength":128}], + "not-rely-on-time": "off", + "max-states-count": ["warn",30], + "no-inline-assembly": "off" }, "plugins": ["prettier"] } diff --git a/solgen/src/bridge/Bridge.sol b/solgen/src/bridge/Bridge.sol index 1e71635c7d..f7d86671ed 100644 --- a/solgen/src/bridge/Bridge.sol +++ b/solgen/src/bridge/Bridge.sol @@ -118,6 +118,10 @@ contract Bridge is OwnableUpgradeable, DelegateCallAware, IBridge { address prevOutbox = activeOutbox; activeOutbox = msg.sender; // We set and reset active outbox around external call so activeOutbox remains valid during call + + // We use a low level call here since we want to bubble up whether it succeeded or failed to the caller + // rather than reverting on failure as well as allow contract and non-contract calls + // solhint-disable-next-line avoid-low-level-calls (success, returnData) = to.call{value: value}(data); activeOutbox = prevOutbox; emit BridgeCallTriggered(msg.sender, to, value, data); diff --git a/solgen/src/bridge/Inbox.sol b/solgen/src/bridge/Inbox.sol index e565c43977..3cc5f5260e 100644 --- a/solgen/src/bridge/Inbox.sol +++ b/solgen/src/bridge/Inbox.sol @@ -207,6 +207,7 @@ contract Inbox is DelegateCallAware, PausableUpgradeable, IInbox { address sender = msg.sender; address destinationAddress = msg.sender; + // solhint-disable-next-line avoid-tx-origin if (!AddressUpgradeable.isContract(sender) && tx.origin == msg.sender) { // isContract check fails if this function is called during a contract's constructor. // We don't adjust the address for calls coming from L1 contracts since their addresses get remapped diff --git a/solgen/src/bridge/Outbox.sol b/solgen/src/bridge/Outbox.sol index d7380dd8bd..5b96f93cc7 100644 --- a/solgen/src/bridge/Outbox.sol +++ b/solgen/src/bridge/Outbox.sol @@ -14,8 +14,8 @@ contract Outbox is DelegateCallAware, IOutbox { address public rollup; // the rollup contract IBridge public bridge; // the bridge contract - mapping(uint256 => bool) spent; // maps leaf number => if spent - mapping(bytes32 => bytes32) roots; // maps root hashes => L2 block hash + mapping(uint256 => bool) public spent; // maps leaf number => if spent + mapping(bytes32 => bytes32) public roots; // maps root hashes => L2 block hash struct L2ToL1Context { uint128 l2Block; diff --git a/solgen/src/challenge/ChallengeManager.sol b/solgen/src/challenge/ChallengeManager.sol index b84c878e3e..3dca5e9a4c 100644 --- a/solgen/src/challenge/ChallengeManager.sol +++ b/solgen/src/challenge/ChallengeManager.sol @@ -15,8 +15,8 @@ contract ChallengeManager is DelegateCallAware, IChallengeManager { using MachineLib for Machine; using ChallengeLib for ChallengeLib.Challenge; - string constant NO_TURN = "NO_TURN"; - uint256 constant MAX_CHALLENGE_DEGREE = 40; + string private constant NO_TURN = "NO_TURN"; + uint256 private constant MAX_CHALLENGE_DEGREE = 40; uint64 public totalChallengesCreated; mapping(uint256 => ChallengeLib.Challenge) public challenges; diff --git a/solgen/src/libraries/AddressAliasHelper.sol b/solgen/src/libraries/AddressAliasHelper.sol index 520c015e5d..625f92b0a1 100644 --- a/solgen/src/libraries/AddressAliasHelper.sol +++ b/solgen/src/libraries/AddressAliasHelper.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.0; library AddressAliasHelper { - uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); /// @notice Utility function that converts the address in the L1 that submitted a tx to /// the inbox to the msg.sender viewed in the L2 @@ -14,7 +14,7 @@ library AddressAliasHelper { /// @return l2Address L2 address as viewed in msg.sender function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { unchecked { - l2Address = address(uint160(l1Address) + offset); + l2Address = address(uint160(l1Address) + OFFSET); } } @@ -24,7 +24,7 @@ library AddressAliasHelper { /// @return l1Address the address in the L1 that triggered the tx to L2 function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { unchecked { - l1Address = address(uint160(l2Address) - offset); + l1Address = address(uint160(l2Address) - OFFSET); } } } diff --git a/solgen/src/mocks/MockResultReceiver.sol b/solgen/src/mocks/MockResultReceiver.sol index abfc7155ca..0bbdaa84d1 100644 --- a/solgen/src/mocks/MockResultReceiver.sol +++ b/solgen/src/mocks/MockResultReceiver.sol @@ -5,7 +5,7 @@ import "../challenge/IChallengeResultReceiver.sol"; import "../challenge/IChallengeManager.sol"; contract MockResultReceiver is IChallengeResultReceiver { - IChallengeManager manager; + IChallengeManager public manager; address public winner; address public loser; uint256 public challengeIndex; diff --git a/solgen/src/osp/HashProofHelper.sol b/solgen/src/osp/HashProofHelper.sol index 79cb00e9d8..53664bdb7e 100644 --- a/solgen/src/osp/HashProofHelper.sol +++ b/solgen/src/osp/HashProofHelper.sol @@ -27,8 +27,8 @@ contract HashProofHelper { bytes part ); - uint256 constant MAX_PART_LENGTH = 32; - uint256 constant KECCAK_ROUND_INPUT = 136; + uint256 private constant MAX_PART_LENGTH = 32; + uint256 private constant KECCAK_ROUND_INPUT = 136; function proveWithFullPreimage(bytes calldata data, uint64 offset) external diff --git a/solgen/src/osp/IOneStepProofEntry.sol b/solgen/src/osp/IOneStepProofEntry.sol index c31e617e21..1b1902a851 100644 --- a/solgen/src/osp/IOneStepProofEntry.sol +++ b/solgen/src/osp/IOneStepProofEntry.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "./IOneStepProver.sol"; library OneStepProofEntryLib { - uint256 constant MAX_STEPS = 1 << 43; + uint256 internal constant MAX_STEPS = 1 << 43; } interface IOneStepProofEntry { diff --git a/solgen/src/osp/OneStepProofEntry.sol b/solgen/src/osp/OneStepProofEntry.sol index c1a1289b77..2dc3798e07 100644 --- a/solgen/src/osp/OneStepProofEntry.sol +++ b/solgen/src/osp/OneStepProofEntry.sol @@ -11,10 +11,10 @@ contract OneStepProofEntry is IOneStepProofEntry { using MerkleProofLib for MerkleProof; using MachineLib for Machine; - IOneStepProver prover0; - IOneStepProver proverMem; - IOneStepProver proverMath; - IOneStepProver proverHostIo; + IOneStepProver public prover0; + IOneStepProver public proverMem; + IOneStepProver public proverMath; + IOneStepProver public proverHostIo; constructor( IOneStepProver prover0_, diff --git a/solgen/src/osp/OneStepProverHostIo.sol b/solgen/src/osp/OneStepProverHostIo.sol index b5e9697819..1763f2ede9 100644 --- a/solgen/src/osp/OneStepProverHostIo.sol +++ b/solgen/src/osp/OneStepProverHostIo.sol @@ -16,8 +16,8 @@ contract OneStepProverHostIo is IOneStepProver { using ValueLib for Value; using ValueStackLib for ValueStack; - uint256 constant LEAF_SIZE = 32; - uint256 constant INBOX_NUM = 2; + uint256 private constant LEAF_SIZE = 32; + uint256 private constant INBOX_NUM = 2; function setLeafByte( bytes32 oldLeaf, @@ -65,10 +65,10 @@ contract OneStepProverHostIo is IOneStepProver { if (inst.opcode == Instructions.GET_GLOBAL_STATE_BYTES32) { mod.moduleMemory.merkleRoot = merkleProof.computeRootFromMemory( leafIdx, - state.bytes32_vals[idx] + state.bytes32Vals[idx] ); } else if (inst.opcode == Instructions.SET_GLOBAL_STATE_BYTES32) { - state.bytes32_vals[idx] = startLeafContents; + state.bytes32Vals[idx] = startLeafContents; } else { revert("BAD_GLOBAL_STATE_OPCODE"); } @@ -82,7 +82,7 @@ contract OneStepProverHostIo is IOneStepProver { return; } - mach.valueStack.push(ValueLib.newI64(state.u64_vals[idx])); + mach.valueStack.push(ValueLib.newI64(state.u64Vals[idx])); } function executeSetU64(Machine memory mach, GlobalState memory state) internal pure { @@ -93,7 +93,7 @@ contract OneStepProverHostIo is IOneStepProver { mach.status = MachineStatus.ERRORED; return; } - state.u64_vals[idx] = val; + state.u64Vals[idx] = val; } function executeReadPreImage( diff --git a/solgen/src/osp/OneStepProverMath.sol b/solgen/src/osp/OneStepProverMath.sol index 4d17beb9e4..8a45011b8c 100644 --- a/solgen/src/osp/OneStepProverMath.sol +++ b/solgen/src/osp/OneStepProverMath.sol @@ -43,7 +43,7 @@ contract OneStepProverMath is IOneStepProver { return uint64(a); } - function I64RelOp( + function i64RelOp( uint64 a, uint64 b, uint16 relop @@ -99,7 +99,7 @@ contract OneStepProverMath is IOneStepProver { b64 = uint64(b); } - bool res = I64RelOp(a64, b64, relop); + bool res = i64RelOp(a64, b64, relop); mach.valueStack.push(ValueLib.newBoolean(res)); } @@ -115,7 +115,7 @@ contract OneStepProverMath is IOneStepProver { uint16 relop = inst.opcode - Instructions.I64_RELOP_BASE; - bool res = I64RelOp(a, b, relop); + bool res = i64RelOp(a, b, relop); mach.valueStack.push(ValueLib.newBoolean(res)); } diff --git a/solgen/src/osp/OneStepProverMemory.sol b/solgen/src/osp/OneStepProverMemory.sol index cd17c4f54d..d785c62f1a 100644 --- a/solgen/src/osp/OneStepProverMemory.sol +++ b/solgen/src/osp/OneStepProverMemory.sol @@ -12,8 +12,8 @@ contract OneStepProverMemory is IOneStepProver { using ValueLib for Value; using ValueStackLib for ValueStack; - uint256 constant LEAF_SIZE = 32; - uint64 constant PAGE_SIZE = 65536; + uint256 private constant LEAF_SIZE = 32; + uint64 private constant PAGE_SIZE = 65536; function pullLeafByte(bytes32 leaf, uint256 idx) internal pure returns (uint8) { require(idx < LEAF_SIZE, "BAD_PULL_LEAF_BYTE_IDX"); diff --git a/solgen/src/rollup/RollupCore.sol b/solgen/src/rollup/RollupCore.sol index 4b35dab653..c304005420 100644 --- a/solgen/src/rollup/RollupCore.sol +++ b/solgen/src/rollup/RollupCore.sol @@ -76,7 +76,7 @@ abstract contract RollupCore is IRollupCore, PausableUpgradeable { Zombie[] private _zombies; mapping(address => uint256) private _withdrawableFunds; - uint256 totalWithdrawableFunds; + uint256 public totalWithdrawableFunds; /** * @notice Get a storage reference to the Node for the given node index diff --git a/solgen/src/rollup/RollupEventBridge.sol b/solgen/src/rollup/RollupEventBridge.sol index f0985dd16b..ac32a7eab1 100644 --- a/solgen/src/rollup/RollupEventBridge.sol +++ b/solgen/src/rollup/RollupEventBridge.sol @@ -34,8 +34,8 @@ contract RollupEventBridge is IMessageProvider, DelegateCallAware { uint8 internal constant REJECT_NODE_EVENT = 2; uint8 internal constant STAKE_CREATED_EVENT = 3; - IBridge bridge; - address rollup; + IBridge public bridge; + address public rollup; modifier onlyRollup() { require(msg.sender == rollup, "ONLY_ROLLUP"); diff --git a/solgen/src/rollup/RollupUserLogic.sol b/solgen/src/rollup/RollupUserLogic.sol index 88632ca611..dcf9da8b2a 100644 --- a/solgen/src/rollup/RollupUserLogic.sol +++ b/solgen/src/rollup/RollupUserLogic.sol @@ -345,7 +345,7 @@ abstract contract AbsRollupUserLogic is ) external override whenNotPaused { // Only the challenge manager contract can call this to declare the winner and loser require(msg.sender == address(challengeManager), "WRONG_SENDER"); - require(challengeIndex == inChallenge(winningStaker, losingStaker)); + require(challengeIndex == inChallenge(winningStaker, losingStaker), "NOT_IN_CHAL"); completeChallengeImpl(winningStaker, losingStaker); } diff --git a/solgen/src/rollup/ValidatorWallet.sol b/solgen/src/rollup/ValidatorWallet.sol index 8b13ad02d3..f7fc2962d5 100644 --- a/solgen/src/rollup/ValidatorWallet.sol +++ b/solgen/src/rollup/ValidatorWallet.sol @@ -39,6 +39,8 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { uint256 numTxes = data.length; for (uint256 i = 0; i < numTxes; i++) { if (data[i].length > 0) require(destination[i].isContract(), "NO_CODE_AT_ADDR"); + // We use a low level call here to allow for contract and non-contract calls + // solhint-disable-next-line avoid-low-level-calls (bool success, ) = address(destination[i]).call{value: amount[i]}(data[i]); if (!success) { assembly { @@ -57,6 +59,8 @@ contract ValidatorWallet is OwnableUpgradeable, DelegateCallAware { uint256 amount ) external payable onlyOwner { if (data.length > 0) require(destination.isContract(), "NO_CODE_AT_ADDR"); + // We use a low level call here to allow for contract and non-contract calls + // solhint-disable-next-line avoid-low-level-calls (bool success, ) = destination.call{value: amount}(data); if (!success) { assembly { diff --git a/solgen/src/state/Deserialize.sol b/solgen/src/state/Deserialize.sol index 41df0fcd99..b322e912cf 100644 --- a/solgen/src/state/Deserialize.sol +++ b/solgen/src/state/Deserialize.sol @@ -235,16 +235,16 @@ library Deserialize { offset = startOffset; // using constant ints for array size requires newer solidity - bytes32[2] memory bytes32_vals; - uint64[2] memory u64_vals; + bytes32[2] memory bytes32Vals; + uint64[2] memory u64Vals; for (uint8 i = 0; i < GlobalStateLib.BYTES32_VALS_NUM; i++) { - (bytes32_vals[i], offset) = b32(proof, offset); + (bytes32Vals[i], offset) = b32(proof, offset); } for (uint8 i = 0; i < GlobalStateLib.U64_VALS_NUM; i++) { - (u64_vals[i], offset) = u64(proof, offset); + (u64Vals[i], offset) = u64(proof, offset); } - state = GlobalState({bytes32_vals: bytes32_vals, u64_vals: u64_vals}); + state = GlobalState({bytes32Vals: bytes32Vals, u64Vals: u64Vals}); } function machine(bytes calldata proof, uint256 startOffset) @@ -255,15 +255,15 @@ library Deserialize { offset = startOffset; MachineStatus status; { - uint8 status_u8; - (status_u8, offset) = u8(proof, offset); - if (status_u8 == 0) { + uint8 statusU8; + (statusU8, offset) = u8(proof, offset); + if (statusU8 == 0) { status = MachineStatus.RUNNING; - } else if (status_u8 == 1) { + } else if (statusU8 == 1) { status = MachineStatus.FINISHED; - } else if (status_u8 == 2) { + } else if (statusU8 == 2) { status = MachineStatus.ERRORED; - } else if (status_u8 == 3) { + } else if (statusU8 == 3) { status = MachineStatus.TOO_FAR; } else { revert("UNKNOWN_MACH_STATUS"); diff --git a/solgen/src/state/GlobalState.sol b/solgen/src/state/GlobalState.sol index ae0db6277e..a040d76635 100644 --- a/solgen/src/state/GlobalState.sol +++ b/solgen/src/state/GlobalState.sol @@ -2,40 +2,40 @@ pragma solidity ^0.8.0; struct GlobalState { - bytes32[2] bytes32_vals; - uint64[2] u64_vals; + bytes32[2] bytes32Vals; + uint64[2] u64Vals; } library GlobalStateLib { - uint16 constant BYTES32_VALS_NUM = 2; - uint16 constant U64_VALS_NUM = 2; + uint16 internal constant BYTES32_VALS_NUM = 2; + uint16 internal constant U64_VALS_NUM = 2; function hash(GlobalState memory state) internal pure returns (bytes32) { return keccak256( abi.encodePacked( "Global state:", - state.bytes32_vals[0], - state.bytes32_vals[1], - state.u64_vals[0], - state.u64_vals[1] + state.bytes32Vals[0], + state.bytes32Vals[1], + state.u64Vals[0], + state.u64Vals[1] ) ); } function getBlockHash(GlobalState memory state) internal pure returns (bytes32) { - return state.bytes32_vals[0]; + return state.bytes32Vals[0]; } function getSendRoot(GlobalState memory state) internal pure returns (bytes32) { - return state.bytes32_vals[1]; + return state.bytes32Vals[1]; } function getInboxPosition(GlobalState memory state) internal pure returns (uint64) { - return state.u64_vals[0]; + return state.u64Vals[0]; } function getPositionInMessage(GlobalState memory state) internal pure returns (uint64) { - return state.u64_vals[1]; + return state.u64Vals[1]; } } diff --git a/solgen/src/state/Instructions.sol b/solgen/src/state/Instructions.sol index 8339a55ea0..e8aa4dcddd 100644 --- a/solgen/src/state/Instructions.sol +++ b/solgen/src/state/Instructions.sol @@ -7,148 +7,148 @@ struct Instruction { } library Instructions { - uint16 constant UNREACHABLE = 0x00; - uint16 constant NOP = 0x01; - uint16 constant BLOCK = 0x02; - uint16 constant BRANCH = 0x0C; - uint16 constant BRANCH_IF = 0x0D; - uint16 constant RETURN = 0x0F; - uint16 constant CALL = 0x10; - uint16 constant CALL_INDIRECT = 0x11; - uint16 constant LOCAL_GET = 0x20; - uint16 constant LOCAL_SET = 0x21; - uint16 constant GLOBAL_GET = 0x23; - uint16 constant GLOBAL_SET = 0x24; - - uint16 constant I32_LOAD = 0x28; - uint16 constant I64_LOAD = 0x29; - uint16 constant F32_LOAD = 0x2A; - uint16 constant F64_LOAD = 0x2B; - uint16 constant I32_LOAD8_S = 0x2C; - uint16 constant I32_LOAD8_U = 0x2D; - uint16 constant I32_LOAD16_S = 0x2E; - uint16 constant I32_LOAD16_U = 0x2F; - uint16 constant I64_LOAD8_S = 0x30; - uint16 constant I64_LOAD8_U = 0x31; - uint16 constant I64_LOAD16_S = 0x32; - uint16 constant I64_LOAD16_U = 0x33; - uint16 constant I64_LOAD32_S = 0x34; - uint16 constant I64_LOAD32_U = 0x35; - - uint16 constant I32_STORE = 0x36; - uint16 constant I64_STORE = 0x37; - uint16 constant F32_STORE = 0x38; - uint16 constant F64_STORE = 0x39; - uint16 constant I32_STORE8 = 0x3A; - uint16 constant I32_STORE16 = 0x3B; - uint16 constant I64_STORE8 = 0x3C; - uint16 constant I64_STORE16 = 0x3D; - uint16 constant I64_STORE32 = 0x3E; - - uint16 constant MEMORY_SIZE = 0x3F; - uint16 constant MEMORY_GROW = 0x40; - - uint16 constant DROP = 0x1A; - uint16 constant SELECT = 0x1B; - uint16 constant I32_CONST = 0x41; - uint16 constant I64_CONST = 0x42; - uint16 constant F32_CONST = 0x43; - uint16 constant F64_CONST = 0x44; - uint16 constant I32_EQZ = 0x45; - uint16 constant I32_RELOP_BASE = 0x46; - uint16 constant IRELOP_EQ = 0; - uint16 constant IRELOP_NE = 1; - uint16 constant IRELOP_LT_S = 2; - uint16 constant IRELOP_LT_U = 3; - uint16 constant IRELOP_GT_S = 4; - uint16 constant IRELOP_GT_U = 5; - uint16 constant IRELOP_LE_S = 6; - uint16 constant IRELOP_LE_U = 7; - uint16 constant IRELOP_GE_S = 8; - uint16 constant IRELOP_GE_U = 9; - uint16 constant IRELOP_LAST = IRELOP_GE_U; - - uint16 constant I64_EQZ = 0x50; - uint16 constant I64_RELOP_BASE = 0x51; - - uint16 constant I32_UNOP_BASE = 0x67; - uint16 constant IUNOP_CLZ = 0; - uint16 constant IUNOP_CTZ = 1; - uint16 constant IUNOP_POPCNT = 2; - uint16 constant IUNOP_LAST = IUNOP_POPCNT; - - uint16 constant I32_ADD = 0x6A; - uint16 constant I32_SUB = 0x6B; - uint16 constant I32_MUL = 0x6C; - uint16 constant I32_DIV_S = 0x6D; - uint16 constant I32_DIV_U = 0x6E; - uint16 constant I32_REM_S = 0x6F; - uint16 constant I32_REM_U = 0x70; - uint16 constant I32_AND = 0x71; - uint16 constant I32_OR = 0x72; - uint16 constant I32_XOR = 0x73; - uint16 constant I32_SHL = 0x74; - uint16 constant I32_SHR_S = 0x75; - uint16 constant I32_SHR_U = 0x76; - uint16 constant I32_ROTL = 0x77; - uint16 constant I32_ROTR = 0x78; - - uint16 constant I64_UNOP_BASE = 0x79; - - uint16 constant I64_ADD = 0x7C; - uint16 constant I64_SUB = 0x7D; - uint16 constant I64_MUL = 0x7E; - uint16 constant I64_DIV_S = 0x7F; - uint16 constant I64_DIV_U = 0x80; - uint16 constant I64_REM_S = 0x81; - uint16 constant I64_REM_U = 0x82; - uint16 constant I64_AND = 0x83; - uint16 constant I64_OR = 0x84; - uint16 constant I64_XOR = 0x85; - uint16 constant I64_SHL = 0x86; - uint16 constant I64_SHR_S = 0x87; - uint16 constant I64_SHR_U = 0x88; - uint16 constant I64_ROTL = 0x89; - uint16 constant I64_ROTR = 0x8A; - - uint16 constant I32_WRAP_I64 = 0xA7; - uint16 constant I64_EXTEND_I32_S = 0xAC; - uint16 constant I64_EXTEND_I32_U = 0xAD; - - uint16 constant I32_REINTERPRET_F32 = 0xBC; - uint16 constant I64_REINTERPRET_F64 = 0xBD; - uint16 constant F32_REINTERPRET_I32 = 0xBE; - uint16 constant F64_REINTERPRET_I64 = 0xBF; - - uint16 constant I32_EXTEND_8S = 0xC0; - uint16 constant I32_EXTEND_16S = 0xC1; - uint16 constant I64_EXTEND_8S = 0xC2; - uint16 constant I64_EXTEND_16S = 0xC3; - uint16 constant I64_EXTEND_32S = 0xC4; - - uint16 constant END_BLOCK = 0x8000; - uint16 constant END_BLOCK_IF = 0x8001; - uint16 constant INIT_FRAME = 0x8002; - uint16 constant ARBITRARY_JUMP_IF = 0x8003; - uint16 constant PUSH_STACK_BOUNDARY = 0x8004; - uint16 constant MOVE_FROM_STACK_TO_INTERNAL = 0x8005; - uint16 constant MOVE_FROM_INTERNAL_TO_STACK = 0x8006; - uint16 constant IS_STACK_BOUNDARY = 0x8007; - uint16 constant DUP = 0x8008; - uint16 constant CROSS_MODULE_CALL = 0x8009; - uint16 constant CALLER_MODULE_INTERNAL_CALL = 0x800A; - - uint16 constant GET_GLOBAL_STATE_BYTES32 = 0x8010; - uint16 constant SET_GLOBAL_STATE_BYTES32 = 0x8011; - uint16 constant GET_GLOBAL_STATE_U64 = 0x8012; - uint16 constant SET_GLOBAL_STATE_U64 = 0x8013; - - uint16 constant READ_PRE_IMAGE = 0x8020; - uint16 constant READ_INBOX_MESSAGE = 0x8021; - uint16 constant HALT_AND_SET_FINISHED = 0x8022; - - uint256 constant INBOX_INDEX_SEQUENCER = 0; - uint256 constant INBOX_INDEX_DELAYED = 1; + uint16 internal constant UNREACHABLE = 0x00; + uint16 internal constant NOP = 0x01; + uint16 internal constant BLOCK = 0x02; + uint16 internal constant BRANCH = 0x0C; + uint16 internal constant BRANCH_IF = 0x0D; + uint16 internal constant RETURN = 0x0F; + uint16 internal constant CALL = 0x10; + uint16 internal constant CALL_INDIRECT = 0x11; + uint16 internal constant LOCAL_GET = 0x20; + uint16 internal constant LOCAL_SET = 0x21; + uint16 internal constant GLOBAL_GET = 0x23; + uint16 internal constant GLOBAL_SET = 0x24; + + uint16 internal constant I32_LOAD = 0x28; + uint16 internal constant I64_LOAD = 0x29; + uint16 internal constant F32_LOAD = 0x2A; + uint16 internal constant F64_LOAD = 0x2B; + uint16 internal constant I32_LOAD8_S = 0x2C; + uint16 internal constant I32_LOAD8_U = 0x2D; + uint16 internal constant I32_LOAD16_S = 0x2E; + uint16 internal constant I32_LOAD16_U = 0x2F; + uint16 internal constant I64_LOAD8_S = 0x30; + uint16 internal constant I64_LOAD8_U = 0x31; + uint16 internal constant I64_LOAD16_S = 0x32; + uint16 internal constant I64_LOAD16_U = 0x33; + uint16 internal constant I64_LOAD32_S = 0x34; + uint16 internal constant I64_LOAD32_U = 0x35; + + uint16 internal constant I32_STORE = 0x36; + uint16 internal constant I64_STORE = 0x37; + uint16 internal constant F32_STORE = 0x38; + uint16 internal constant F64_STORE = 0x39; + uint16 internal constant I32_STORE8 = 0x3A; + uint16 internal constant I32_STORE16 = 0x3B; + uint16 internal constant I64_STORE8 = 0x3C; + uint16 internal constant I64_STORE16 = 0x3D; + uint16 internal constant I64_STORE32 = 0x3E; + + uint16 internal constant MEMORY_SIZE = 0x3F; + uint16 internal constant MEMORY_GROW = 0x40; + + uint16 internal constant DROP = 0x1A; + uint16 internal constant SELECT = 0x1B; + uint16 internal constant I32_CONST = 0x41; + uint16 internal constant I64_CONST = 0x42; + uint16 internal constant F32_CONST = 0x43; + uint16 internal constant F64_CONST = 0x44; + uint16 internal constant I32_EQZ = 0x45; + uint16 internal constant I32_RELOP_BASE = 0x46; + uint16 internal constant IRELOP_EQ = 0; + uint16 internal constant IRELOP_NE = 1; + uint16 internal constant IRELOP_LT_S = 2; + uint16 internal constant IRELOP_LT_U = 3; + uint16 internal constant IRELOP_GT_S = 4; + uint16 internal constant IRELOP_GT_U = 5; + uint16 internal constant IRELOP_LE_S = 6; + uint16 internal constant IRELOP_LE_U = 7; + uint16 internal constant IRELOP_GE_S = 8; + uint16 internal constant IRELOP_GE_U = 9; + uint16 internal constant IRELOP_LAST = IRELOP_GE_U; + + uint16 internal constant I64_EQZ = 0x50; + uint16 internal constant I64_RELOP_BASE = 0x51; + + uint16 internal constant I32_UNOP_BASE = 0x67; + uint16 internal constant IUNOP_CLZ = 0; + uint16 internal constant IUNOP_CTZ = 1; + uint16 internal constant IUNOP_POPCNT = 2; + uint16 internal constant IUNOP_LAST = IUNOP_POPCNT; + + uint16 internal constant I32_ADD = 0x6A; + uint16 internal constant I32_SUB = 0x6B; + uint16 internal constant I32_MUL = 0x6C; + uint16 internal constant I32_DIV_S = 0x6D; + uint16 internal constant I32_DIV_U = 0x6E; + uint16 internal constant I32_REM_S = 0x6F; + uint16 internal constant I32_REM_U = 0x70; + uint16 internal constant I32_AND = 0x71; + uint16 internal constant I32_OR = 0x72; + uint16 internal constant I32_XOR = 0x73; + uint16 internal constant I32_SHL = 0x74; + uint16 internal constant I32_SHR_S = 0x75; + uint16 internal constant I32_SHR_U = 0x76; + uint16 internal constant I32_ROTL = 0x77; + uint16 internal constant I32_ROTR = 0x78; + + uint16 internal constant I64_UNOP_BASE = 0x79; + + uint16 internal constant I64_ADD = 0x7C; + uint16 internal constant I64_SUB = 0x7D; + uint16 internal constant I64_MUL = 0x7E; + uint16 internal constant I64_DIV_S = 0x7F; + uint16 internal constant I64_DIV_U = 0x80; + uint16 internal constant I64_REM_S = 0x81; + uint16 internal constant I64_REM_U = 0x82; + uint16 internal constant I64_AND = 0x83; + uint16 internal constant I64_OR = 0x84; + uint16 internal constant I64_XOR = 0x85; + uint16 internal constant I64_SHL = 0x86; + uint16 internal constant I64_SHR_S = 0x87; + uint16 internal constant I64_SHR_U = 0x88; + uint16 internal constant I64_ROTL = 0x89; + uint16 internal constant I64_ROTR = 0x8A; + + uint16 internal constant I32_WRAP_I64 = 0xA7; + uint16 internal constant I64_EXTEND_I32_S = 0xAC; + uint16 internal constant I64_EXTEND_I32_U = 0xAD; + + uint16 internal constant I32_REINTERPRET_F32 = 0xBC; + uint16 internal constant I64_REINTERPRET_F64 = 0xBD; + uint16 internal constant F32_REINTERPRET_I32 = 0xBE; + uint16 internal constant F64_REINTERPRET_I64 = 0xBF; + + uint16 internal constant I32_EXTEND_8S = 0xC0; + uint16 internal constant I32_EXTEND_16S = 0xC1; + uint16 internal constant I64_EXTEND_8S = 0xC2; + uint16 internal constant I64_EXTEND_16S = 0xC3; + uint16 internal constant I64_EXTEND_32S = 0xC4; + + uint16 internal constant END_BLOCK = 0x8000; + uint16 internal constant END_BLOCK_IF = 0x8001; + uint16 internal constant INIT_FRAME = 0x8002; + uint16 internal constant ARBITRARY_JUMP_IF = 0x8003; + uint16 internal constant PUSH_STACK_BOUNDARY = 0x8004; + uint16 internal constant MOVE_FROM_STACK_TO_INTERNAL = 0x8005; + uint16 internal constant MOVE_FROM_INTERNAL_TO_STACK = 0x8006; + uint16 internal constant IS_STACK_BOUNDARY = 0x8007; + uint16 internal constant DUP = 0x8008; + uint16 internal constant CROSS_MODULE_CALL = 0x8009; + uint16 internal constant CALLER_MODULE_INTERNAL_CALL = 0x800A; + + uint16 internal constant GET_GLOBAL_STATE_BYTES32 = 0x8010; + uint16 internal constant SET_GLOBAL_STATE_BYTES32 = 0x8011; + uint16 internal constant GET_GLOBAL_STATE_U64 = 0x8012; + uint16 internal constant SET_GLOBAL_STATE_U64 = 0x8013; + + uint16 internal constant READ_PRE_IMAGE = 0x8020; + uint16 internal constant READ_INBOX_MESSAGE = 0x8021; + uint16 internal constant HALT_AND_SET_FINISHED = 0x8022; + + uint256 internal constant INBOX_INDEX_SEQUENCER = 0; + uint256 internal constant INBOX_INDEX_DELAYED = 1; function hash(Instruction memory inst) internal pure returns (bytes32) { return keccak256(abi.encodePacked("Instruction:", inst.opcode, inst.argumentData)); From 96c22379fb74f14ddd4998b5f86b35f15ac19ada Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Tue, 22 Feb 2022 22:05:33 -0600 Subject: [PATCH 103/110] use L1 for fees test --- go-ethereum | 2 +- system_tests/common_test.go | 25 +++++++++++++ system_tests/delayedinbox_test.go | 22 +---------- system_tests/fees_test.go | 62 ++++++++++++++++++++----------- 4 files changed, 67 insertions(+), 44 deletions(-) diff --git a/go-ethereum b/go-ethereum index 555c97f09d..e55e6310bd 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 555c97f09df86bc0ca3bfdfc1c96c1f8f91fde4f +Subproject commit e55e6310bd367ec34ee61b8591db835c87592c55 diff --git a/system_tests/common_test.go b/system_tests/common_test.go index bbb5a52621..0976019a14 100644 --- a/system_tests/common_test.go +++ b/system_tests/common_test.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/arbstate/arbnode" + "github.com/offchainlabs/arbstate/solgen/go/bridgegen" "github.com/offchainlabs/arbstate/solgen/go/precompilesgen" "github.com/offchainlabs/arbstate/util/testhelpers" ) @@ -57,6 +58,30 @@ func TransferBalance(t *testing.T, from, to string, amount *big.Int, l2info info Require(t, err) } +func SendSignedTxViaL1(t *testing.T, ctx context.Context, l1info *BlockchainTestInfo, l1client arbutil.L1Interface, l2client arbutil.L1Interface, delayedTx *types.Transaction) *types.Receipt { + delayedInboxContract, err := bridgegen.NewInbox(l1info.GetAddress("Inbox"), l1client) + Require(t, err) + usertxopts := l1info.GetDefaultTransactOpts("User") + + txbytes, err := delayedTx.MarshalBinary() + Require(t, err) + txwrapped := append([]byte{arbos.L2MessageKind_SignedTx}, txbytes...) + l1tx, err := delayedInboxContract.SendL2Message(&usertxopts, txwrapped) + Require(t, err) + _, err = arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) + Require(t, err) + + // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in + for i := 0; i < 30; i++ { + SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ + l1info.PrepareTx("Faucet", "Faucet", 30000, big.NewInt(1e12), nil), + }) + } + receipt, err := arbutil.WaitForTx(ctx, l2client, delayedTx.Hash(), time.Second*5) + Require(t, err) + return receipt +} + func GetBaseFee(t *testing.T, client client, ctx context.Context) *big.Int { header, err := client.HeaderByNumber(ctx, nil) Require(t, err) diff --git a/system_tests/delayedinbox_test.go b/system_tests/delayedinbox_test.go index 858f70adca..21db63e67f 100644 --- a/system_tests/delayedinbox_test.go +++ b/system_tests/delayedinbox_test.go @@ -9,12 +9,10 @@ import ( "math/big" "strings" "testing" - "time" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/core/types" "github.com/offchainlabs/arbstate/arbos" - "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/bridgegen" ) @@ -46,26 +44,8 @@ func TestDelayInboxSimple(t *testing.T) { l2info.GenerateAccount("User2") delayedTx := l2info.PrepareTx("Owner", "User2", 50001, big.NewInt(1e6), nil) + SendSignedTxViaL1(t, ctx, l1info, l1client, l2client, delayedTx) - delayedInboxContract, err := bridgegen.NewInbox(l1info.GetAddress("Inbox"), l1client) - Require(t, err) - usertxopts := l1info.GetDefaultTransactOpts("User") - txbytes, err := delayedTx.MarshalBinary() - Require(t, err) - txwrapped := append([]byte{arbos.L2MessageKind_SignedTx}, txbytes...) - l1tx, err := delayedInboxContract.SendL2Message(&usertxopts, txwrapped) - Require(t, err) - _, err = arbutil.EnsureTxSucceeded(ctx, l1client, l1tx) - Require(t, err) - - // sending l1 messages creates l1 blocks.. make enough to get that delayed inbox message in - for i := 0; i < 30; i++ { - SendWaitTestTransactions(t, ctx, l1client, []*types.Transaction{ - l1info.PrepareTx("Faucet", "User", 30000, big.NewInt(1e12), nil), - }) - } - _, err = arbutil.WaitForTx(ctx, l2client, delayedTx.Hash(), time.Second*5) - Require(t, err) l2balance, err := l2client.BalanceAt(ctx, l2info.GetAddress("User2"), nil) Require(t, err) if l2balance.Cmp(big.NewInt(1e6)) != 0 { diff --git a/system_tests/fees_test.go b/system_tests/fees_test.go index b26ffb82df..e8e2327219 100644 --- a/system_tests/fees_test.go +++ b/system_tests/fees_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/offchainlabs/arbstate/arbnode" - "github.com/offchainlabs/arbstate/solgen/go/mocksgen" + "github.com/ethereum/go-ethereum/core/types" + "github.com/offchainlabs/arbstate/arbutil" "github.com/offchainlabs/arbstate/solgen/go/precompilesgen" "github.com/offchainlabs/arbstate/util" "github.com/offchainlabs/arbstate/util/colors" @@ -20,40 +20,45 @@ import ( func TestTips(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + l2info, _, l2client, l1info, _, l1client, stack := CreateTestNodeOnL1(t, ctx, true) + defer stack.Close() - l2info, _, client := CreateTestL2(t, ctx) auth := l2info.GetDefaultTransactOpts("Owner") callOpts := l2info.GetDefaultCallOpts("Owner") aggregator := testhelpers.RandomAddress() + // get the network fee account + arbOwnerPublic, err := precompilesgen.NewArbOwnerPublic(common.HexToAddress("0x6b"), l2client) + Require(t, err, "could not deploy ArbOwner contract") + networkFeeAccount, err := arbOwnerPublic.GetNetworkFeeAccount(callOpts) + Require(t, err, "could not get the network fee account") + // set a preferred aggregator who won't be the one to post the tx - arbAggregator, err := precompilesgen.NewArbAggregator(common.HexToAddress("0x6d"), client) + arbAggregator, err := precompilesgen.NewArbAggregator(common.HexToAddress("0x6d"), l2client) Require(t, err, "could not deploy ArbAggregator contract") tx, err := arbAggregator.SetPreferredAggregator(&auth, aggregator) Require(t, err, "could not set L2 gas price") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, l2client, tx) Require(t, err) - // get the network fee account - arbOwner, err := precompilesgen.NewArbOwner(common.HexToAddress("0x70"), client) - Require(t, err, "could not deploy ArbOwner contract") - networkFeeAccount, err := arbOwner.GetNetworkFeeAccount(callOpts) - Require(t, err, "could not get the network fee account") - - networkBefore := GetBalance(t, ctx, client, networkFeeAccount) - colors.PrintMint("network: ", networkFeeAccount, networkBefore) + basefee := GetBaseFee(t, l2client, ctx) + auth.GasFeeCap = util.BigMulByUfrac(basefee, 5, 4) // add room for a 20% tip + auth.GasTipCap = util.BigMulByUfrac(basefee, 1, 4) // add a 20% tip - auth.GasFeeCap = util.BigMulByUfrac(l2info.GasPrice, 5, 4) // add room for a 20% tip - auth.GasTipCap = util.BigMulByUfrac(l2info.GasPrice, 1, 4) // add a 20% tip + networkBefore := GetBalance(t, ctx, l2client, networkFeeAccount) - _, tx, _, err = mocksgen.DeploySimple(&auth, client) - Require(t, err, "could not deploy contract") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + // use L1 to post a message since the sequencer won't do it + nosend := auth + nosend.NoSend = true + tx, err = arbAggregator.SetPreferredAggregator(&nosend, aggregator) Require(t, err) + receipt := SendSignedTxViaL1(t, ctx, l1info, l1client, l2client, tx) + if receipt.Status != types.ReceiptStatusSuccessful { + Fail(t, "failed to prefer the sequencer") + } - networkAfter := GetBalance(t, ctx, client, networkFeeAccount) - colors.PrintMint("network: ", networkFeeAccount, networkAfter) - + networkAfter := GetBalance(t, ctx, l2client, networkFeeAccount) + colors.PrintMint("network: ", networkFeeAccount, networkBefore, networkAfter) colors.PrintBlue("pricing: ", l2info.GasPrice, auth.GasFeeCap, auth.GasTipCap) colors.PrintBlue("payment: ", tx.GasPrice(), tx.GasFeeCap(), tx.GasTipCap()) @@ -61,6 +66,19 @@ func TestTips(t *testing.T) { Fail(t, "user did not pay the tip") } + tip := util.BigMulByUint(util.BigSub(tx.GasPrice(), basefee), receipt.GasUsed) + full := util.BigMulByUint(tx.GasPrice(), receipt.GasUsed) + networkRevenue := util.BigSub(networkAfter, networkBefore) + colors.PrintMint("tip: ", tip, full, networkRevenue) + + colors.PrintRed("used: ", receipt.GasUsed, basefee) + + if !util.BigEquals(tip, util.BigMulByFrac(networkRevenue, 1, 5)) { + Fail(t, "1/5th of the network's revenue should be the tip") + } + if !util.BigEquals(full, networkRevenue) { + Fail(t, "the network didn't receive the tip") + } } // Test that the sequencer won't subvert a user's aggregation preferences @@ -76,7 +94,7 @@ func TestSequencerWontPostWhenNotPreferred(t *testing.T) { Require(t, err, "could not deploy ArbAggregator contract") tx, err := arbAggregator.SetPreferredAggregator(&auth, testhelpers.RandomAddress()) Require(t, err, "could not set L2 gas price") - _, err = arbnode.EnsureTxSucceeded(ctx, client, tx) + _, err = arbutil.EnsureTxSucceeded(ctx, client, tx) Require(t, err) // get the network fee account From 77e95ec265dc56bfd5955504eda1d9009b1b48da Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 22:11:05 -0600 Subject: [PATCH 104/110] Document ChallengeManager (replaces old challenge docs) --- docs/proving/BlockChallenge.md | 16 ------- docs/proving/ChallengeCore.md | 36 -------------- docs/proving/ChallengeManager.md | 76 ++++++++++++++++++++++++++++++ docs/proving/ExecutionChallenge.md | 19 -------- 4 files changed, 76 insertions(+), 71 deletions(-) delete mode 100644 docs/proving/BlockChallenge.md delete mode 100644 docs/proving/ChallengeCore.md create mode 100644 docs/proving/ChallengeManager.md delete mode 100644 docs/proving/ExecutionChallenge.md diff --git a/docs/proving/BlockChallenge.md b/docs/proving/BlockChallenge.md deleted file mode 100644 index c25030fde5..0000000000 --- a/docs/proving/BlockChallenge.md +++ /dev/null @@ -1,16 +0,0 @@ -# BlockChallenge - -The `BlockChallenge` is an instance of a `ChallengeCore` (see `ChallengeCore.md` for details). -For this challenge type, a step is the creation of a single block. -It begins with a start global state, a start machine status, an end global state, and an end machine status. -It stores the start global state and end global state in storage so they can be accessed later. -Its challenge unit is a "block state", which consists of a global state and a machine status. -The block state hash function can be found in `ChallengeLib` `blockStateHash`. - -Once the BlockChallenge has been bisected down to an individual step, -`challengeExecution` can be called by the current responder. -This operates similarly to a bisection in that the responder must provide a competing global state and machine state, -but it uses that information to create an `ExecutionChallenge` and transfer control to it. -From that point on, the BlockChallenge is inacessible, and any operations must go through the ExecutionChallenge. -The dispute between the two parties will eventually be resolved via the ExecutionChallenge. -See `ExecutionChallenge.md` for details. diff --git a/docs/proving/ChallengeCore.md b/docs/proving/ChallengeCore.md deleted file mode 100644 index 37e67ce75f..0000000000 --- a/docs/proving/ChallengeCore.md +++ /dev/null @@ -1,36 +0,0 @@ -# ChallengeCore - -_**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ - -`ChallengeCore` is an abstract contract which contains the basis for bisecting down to a single point in a challenge. -A generic challenge must be over something which advances from one hash to another. A hash of the state at -a single point in time is refered to as a segment. -In practice, a challenge is either a block challenge (where the unit step is creating and -processing one block), -or an execution challenge (where the unit step is executing one WAVM instruction). - -The `ChallengeLib` helper library contains a `hashChallengeState` method which hashes a list of segment hashes, -a start position, and a total segments length. -This is enough information to infer the position of each segment hash. -The challenge "degree" refers to the number of segment hashes minus one. -The distance (in steps) between one segment and the next is `floor(segmentsLength / degree)`, except for the -last pair of segments, where `segmentsLength % degree` is added to the normal distance, so that -the total distance is `segmentsLength`. - -A challenge begins with only two segments (a degree of one), which is the asserter's initial assertion. -Then, the bisection game begins on the challenger's turn. -In each round of the game, the current responder must choose an adjacent pair of segments to challenge. -By doing so, they are disputing their opponent's claim that starting with the first segment and executing -for the specified distance (number of steps) will result in the second segment. At this point the two parties -agree on the correctness of the first segment but disagree about the correctness of the second segment. -The responder must provide a bisection with a start segment equal to the first segment, but an end segment -different from the second segment. -In doing so, they break the challenge down into smaller distances, and it becomes their opponent's turn. -Each bisection must have degree `min(40, numStepsInChallengedSegment)`, ensuring the challenge makes progress. - -In addition, a segment with a length of only one step cannot be bisected. -That case is challenge type specific, as it depends on the nature of a step. - -Note that unlike in a traditional bisection protocol, where one party proposes segments and the other decides which to challenge, -this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections -when challenging. diff --git a/docs/proving/ChallengeManager.md b/docs/proving/ChallengeManager.md new file mode 100644 index 0000000000..9ba7dc204e --- /dev/null +++ b/docs/proving/ChallengeManager.md @@ -0,0 +1,76 @@ +# ChallengeManager + +The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: + +```mermaid +flowchart LR + B[Block challenge] + E[Execution challenge] + W[Waiting for timeout to allow for an emergency upgrade] + Start --> B + B -->|bisectExecution| B + B -->|challengeExecution| E + B -->|challengeExecution from non-finished state| W + E -->|bisectExecution| E + E -->|oneStepProveExecution| W + W -->|timeout| End + B -->|timeout| End + E -->|timeout| End +``` + +## Block challenge + +The challenge begins by bisecting over global states (including block hashes). +Before actual machine execution is disputed, the dispute is narrowed down to an individual block. +Once the challenge has been bisected down to an individual block, +`challengeExecution` can be called by the current responder. +This operates similarly to a bisection in that the responder must provide a competing global state and machine state, +but it uses that information to transition to the execution challenge phase. + +## Execution challenge + +Once narrowed down to an individual block, the actual machine execution can be bisected. +Once the execution has been bisected down to an individual step, +`oneStepProveExecution` can be called by the current responder. +The current responder must provide proof data to execute a step of the machine. +If executing that step ends in a different state than was previously asserted, +the current responder wins the challenge. + +## General bisection protocol + +_**Note:** the term bisection in this document is used for clarity but refers to a dissection of any degree._ + +The `ChallengeLib` helper library contains a `hashChallengeState` method which hashes a list of segment hashes, +a start position, and a total segments length, which generates the `ChallengeLib.Challenge`'s `challengeStateHash`. +This is enough information to infer the position of each segment hash. +The challenge "degree" refers to the number of segment hashes minus one. +The distance (in steps) between one segment and the next is `floor(segmentsLength / degree)`, except for the +last pair of segments, where `segmentsLength % degree` is added to the normal distance, so that +the total distance is `segmentsLength`. + +A challenge begins with only two segments (a degree of one), which is the asserter's initial assertion. +Then, the bisection game begins on the challenger's turn. +In each round of the game, the current responder must choose an adjacent pair of segments to challenge. +By doing so, they are disputing their opponent's claim that starting with the first segment and executing +for the specified distance (number of steps) will result in the second segment. At this point the two parties +agree on the correctness of the first segment but disagree about the correctness of the second segment. +The responder must provide a bisection with a start segment equal to the first segment, but an end segment +different from the second segment. +In doing so, they break the challenge down into smaller distances, and it becomes their opponent's turn. +Each bisection must have degree `min(40, numStepsInChallengedSegment)`, ensuring the challenge makes progress. + +In addition, a segment with a length of only one step cannot be bisected. +What happens there is specific to the phase of the challenge, as either a `challengeExecution` or `oneStepProveExecution`. + +Note that unlike in a traditional bisection protocol, where one party proposes segments and the other decides which to challenge, +this protocol is symmetric in that both players take turns deciding where to challenge and proposing bisections +when challenging. + +## Winning the challenge + +Note that for the time being, winning the challenge isn't instant. +Instead, it simply makes the current responder the winner's opponent, +and sets the state hash to 0. In that state the party does not have any +valid moves, so it will eventually lose by timeout. +This is done as a precaution, so that if a challenge is resolved incorrectly, +there is time to diagnose and fix the error with a contract upgrade. diff --git a/docs/proving/ExecutionChallenge.md b/docs/proving/ExecutionChallenge.md deleted file mode 100644 index efb7c6ca1a..0000000000 --- a/docs/proving/ExecutionChallenge.md +++ /dev/null @@ -1,19 +0,0 @@ -# ExecutionChallenge - -The `ExecutionChallenge` is an instance of a `ChallengeCore` (see `ChallengeCore.md` for details). -It's instantiated from a BlockChallenge with a start and end machine hash. -Its challenge unit is a machine, which is hashed in `Machines.sol`. Its step is the execution of -a single WAVM instruction. - -Once the ExecutionChallenge has been bisected down to an individual step, -`oneStepProveExecution` can be called by the current responder. -The current responder must provide proof data to execute a step of the machine. -If executing that step ends in a different state than was previously asserted, -the current responder wins the challenge. - -Note that for the time being, winning the challenge isn't instant. -Instead, it simply makes the current responder the winner's opponent, -and sets the state hash to 0. In that state the party does not have any -value moves, so it will eventually lose by timeout. -This is done as a precaution, so that if a challenge is resolved incorrectly, -there is time to diagnose and fix the error with a contract upgrade. From d9237b74554023e449c79313bf1387136a462516 Mon Sep 17 00:00:00 2001 From: Rachel Franks Date: Tue, 22 Feb 2022 22:38:45 -0600 Subject: [PATCH 105/110] sort tx types --- docs/arbos/Geth.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/arbos/Geth.md b/docs/arbos/Geth.md index 5f575369db..5431ae9aa8 100644 --- a/docs/arbos/Geth.md +++ b/docs/arbos/Geth.md @@ -114,9 +114,9 @@ Nitro geth includes a few L2-specific transaction types. Click on any to jump to |:--------------------------------------------------|:-------------------------------------|:---------------------------|--------| | [`ArbitrumUnsignedTx`][ArbTxUnsigned] | An L1 to L2 message | [`EndTxHook`][HE] | Bridge | | [`ArbitrumContractTx`][ArbTxContract] | A nonce-less L1 to L2 message   | [`EndTxHook`][HE] | Bridge | +| [`ArbitrumDepositTx`][ArbTxDeposit] | A user deposit | [`StartTxHook`][HS] | Bridge | | [`ArbitrumSubmitRetryableTx`][ArbTxSubmit]   | Creating a retryable | [`StartTxHook`][HS]   | Bridge | | [`ArbitrumRetryTx`][ArbTxRetry] | A retryable redeem attempt | [`EndTxHook`][HE] | L2 | -| [`ArbitrumDepositTx`][ArbTxDeposit] | A user deposit | [`StartTxHook`][HS] | Bridge | | [`ArbitrumInternalTx`][ArbTxInternal] | ArbOS state update | [`StartTxHook`][HS] | ArbOS | [ArbTxUnsigned]: #ArbitrumUnsignedTx @@ -136,15 +136,15 @@ Provides a mechanism for a user on L1 to message a contract on L2. This uses the ### [`ArbitrumContractTx`][ArbitrumContractTx_link] These are like an [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link] but are intended for smart contracts. These use the bridge's unique, sequential nonce rather than requiring the caller specify their own. An L1 contract may still use an [`ArbitrumUnsignedTx`][ArbitrumUnsignedTx_link], but doing so may necessitate tracking the nonce in L1 state. +### [`ArbitrumDepositTx`][ArbitrumDepositTx_link] +Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. + ### [`ArbitrumSubmitRetryableTx`][ArbitrumSubmitRetryableTx_link] Represents a retryable submission and may schedule an [`ArbitrumRetryTx`](#ArbitrumRetryTx) if provided enough gas. Please see the [retryables documentation](ArbOS.md#Retryables) for more info. ### [`ArbitrumRetryTx`][ArbitrumRetryTx_link] These are scheduled by calls to the [`redeem`](Precompiles.md#ArbRetryableTx) precompile method and via retryable auto-redemption. Please see the [retryables documentation](ArbOS.md#Retryables) for more info. -### [`ArbitrumDepositTx`][ArbitrumDepositTx_link] -Represents a user deposit from L1 to L2. This increases the user's balance by the amount deposited on L1. - ### [`ArbitrumInternalTx`][ArbitrumInternalTx_link] Because tracing support requires ArbOS's state-changes happen inside a transaction, ArbOS may create a tx of this type to update its state in-between user-generated transactions. Such a tx has a [`Type`][InternalType_link] field signifying the state it will update, though currently this is just future-proofing as there's only one value it may have. Below are the internal tx types. From 4dbe9bdb77124979341349515d1efb5266e4add2 Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Tue, 22 Feb 2022 23:40:41 -0500 Subject: [PATCH 106/110] Add solhint to makefile --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index a604cdb8e0..bf4f1d8675 100644 --- a/Makefile +++ b/Makefile @@ -251,6 +251,7 @@ solgen/test/proofs/%.json: arbitrator/prover/test-cases/%.wasm $(arbitrator_prov .make/lint: build-node-deps | .make golangci-lint run --fix + yarn --cwd solgen solhint @touch $@ .make/fmt: build-node-deps .make/yarndeps | .make From d946a3fef58cb8e4f5be2331a48ef76ad9fe477c Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 23:05:08 -0600 Subject: [PATCH 107/110] Merge in upstream geth v1.10.16 --- arbnode/node.go | 4 +- arbnode/transaction_streamer.go | 4 +- go-ethereum | 2 +- go.mod | 27 +++---- go.sum | 124 +++----------------------------- 5 files changed, 25 insertions(+), 136 deletions(-) diff --git a/arbnode/node.go b/arbnode/node.go index baf250ada7..1cc03fcd7b 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -625,7 +625,7 @@ func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig return GetBlockChain(chainDb, cacheConfig, config) } -// TODO: is that right? -func shouldPreserveFalse(block *types.Block) bool { +// Don't preserve reorg'd out blocks +func shouldPreserveFalse(header *types.Header) bool { return false } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index b147181007..c958e42580 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -386,7 +386,7 @@ func (s *TransactionStreamer) SequenceTransactions(header *arbos.L1IncomingMessa for _, receipt := range receipts { logs = append(logs, receipt.Logs...) } - status, err := s.bc.WriteBlockWithState(block, receipts, logs, statedb, true) + status, err := s.bc.WriteBlockAndSetHead(block, receipts, logs, statedb, true) if err != nil { return err } @@ -572,7 +572,7 @@ func (s *TransactionStreamer) createBlocks(ctx context.Context) error { for _, receipt := range receipts { logs = append(logs, receipt.Logs...) } - status, err := s.bc.WriteBlockWithState(block, receipts, logs, statedb, true) + status, err := s.bc.WriteBlockAndSetHead(block, receipts, logs, statedb, true) if err != nil { return err } diff --git a/go-ethereum b/go-ethereum index e55e6310bd..c074ce0b9f 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit e55e6310bd367ec34ee61b8591db835c87592c55 +Subproject commit c074ce0b9f64c4123572a158cb4b4abea3ca0666 diff --git a/go.mod b/go.mod index 6615e7bf95..4261ad9cd6 100644 --- a/go.mod +++ b/go.mod @@ -8,43 +8,36 @@ replace github.com/ethereum/go-ethereum => ./go-ethereum require ( github.com/andybalholm/brotli v1.0.3 + github.com/btcsuite/btcd v0.20.1-beta github.com/ethereum/go-ethereum v1.10.13-0.20211112145008-abc74a5ffeb7 - github.com/miguelmota/go-solidity-sha3 v0.1.1 + github.com/pkg/errors v0.9.1 ) require ( - github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.1.0 // indirect - github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 // indirect - github.com/knadh/koanf v1.3.2 // indirect - github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/rhnvrm/simples3 v0.6.1 // indirect - github.com/rs/zerolog v1.26.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/gobwas/ws v1.1.0 + github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 + github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f ) require ( github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect - github.com/btcsuite/btcd v0.20.1-beta // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea // indirect + github.com/deckarep/golang-set v1.8.0 // indirect github.com/deepmap/oapi-codegen v1.8.2 // indirect - github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1 // indirect github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect github.com/go-ole/go-ole v1.2.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.5.6 // indirect github.com/google/uuid v1.1.5 // indirect github.com/gorilla/websocket v1.4.2 // indirect - github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 // indirect + github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect @@ -53,7 +46,7 @@ require ( github.com/influxdata/influxdb v1.8.3 // indirect github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect - github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect @@ -62,7 +55,6 @@ require ( github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opentracing/opentracing-go v1.1.0 // indirect github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/tsdb v0.7.1 // indirect github.com/rjeczalik/notify v0.9.2 // indirect github.com/rs/cors v1.7.0 // indirect @@ -79,7 +71,6 @@ require ( golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect - gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 // indirect gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index c306b58c1c..6112eb36cf 100644 --- a/go.sum +++ b/go.sum @@ -43,43 +43,26 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= -github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.3 h1:fpcw+r1N1h0Poc1F/pHbW40cUm/lMEQslZtCkBQ0UnM= github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= -github.com/aws/aws-sdk-go-v2 v1.9.2 h1:dUFQcMNZMLON4BOe273pl0filK9RqyQMhCK/6xssL6s= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.0.2/go.mod h1:3hGg3PpiEjHnrkrlasTfxFqUsZ2GCk/fMUn4CbKgSkM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7NkwbjlijluLsrIbu/iyl35RO4= github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/btcsuite/btcd v0.0.0-20190109040709-5bda5314ca95/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= @@ -101,8 +84,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= @@ -111,31 +92,25 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea h1:j4317fAZh7X6GqbFowYdYdI0L9bwxL07jyPZIdepyZ0= -github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU= github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf h1:sh8rkQZavChcmakYiSlqu2425CHyFXLZZnvm7PDpU8M= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= -github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ= github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= -github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1 h1:YQOLTC8zvFaNSEuMexG0i7pY26bOksnQFsSJfGclo54= -github.com/dvyukov/go-fuzz v0.0.0-20210914135545-4980593459a1/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= @@ -154,7 +129,6 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -162,12 +136,10 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= @@ -176,11 +148,9 @@ github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 h1:XC9N1eiAyO1zg62dpOU8bex8emB/zluUtKcbLNjJxGI= github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484/go.mod h1:5nDZF4afNA1S7ZKcBXCMvDo4nuCTp1931DND7/W4aXo= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -200,7 +170,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= @@ -212,8 +181,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -229,33 +198,14 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M= -github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= +github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= -github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= @@ -280,13 +230,12 @@ github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19y github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= -github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -297,8 +246,7 @@ github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= -github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= +github.com/karalabe/usb v0.0.2/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= @@ -306,17 +254,15 @@ github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/knadh/koanf v1.3.2 h1:0JKfmTLcvEmdJwjY16BMOVKpqThxRwj29CtQvZiCsAA= -github.com/knadh/koanf v1.3.2/go.mod h1:HZ7HMLIGbrWJUfgtEzfHvzR/rX+eIqQlBNPRr4Vt42s= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= @@ -335,7 +281,6 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= @@ -347,26 +292,11 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miguelmota/go-solidity-sha3 v0.1.1 h1:3Y08sKZDtudtE5kbTBPC9RYJznoSYyWI9VD6mghU0CA= -github.com/miguelmota/go-solidity-sha3 v0.1.1/go.mod h1:sax1FvQF+f71j8W1uUHMZn8NxKyl5rYLks2nqj8RFEw= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= @@ -375,8 +305,6 @@ github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/offchainlabs/go-solidity-sha3 v0.1.2/go.mod h1:WYAU7UTm1wXzEhnsTt843T77fKfR9x/rqqzjm8NJ3Mc= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -393,9 +321,7 @@ github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= @@ -408,7 +334,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -422,23 +347,13 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rhnvrm/simples3 v0.6.1 h1:H0DJwybR6ryQE+Odi9eqkHuzjYAeJgtGcGtuBwOhsH8= -github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= -github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho= github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8= github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= -github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE= -github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -452,8 +367,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -481,7 +394,6 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -495,7 +407,6 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= @@ -568,18 +479,15 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -597,7 +505,6 @@ golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -609,7 +516,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 h1:xrCZDmdtoloIiooiA9q0OQb9r8HejIHYoHGhGCe1pGg= golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -617,7 +523,6 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -656,7 +561,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -682,7 +586,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -695,11 +598,9 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -709,18 +610,15 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= From 950521444c9d09527e347ab5ac5fc55454390345 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Tue, 22 Feb 2022 23:18:47 -0600 Subject: [PATCH 108/110] Switch flowchart from left-to-right to top-down --- docs/proving/ChallengeManager.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proving/ChallengeManager.md b/docs/proving/ChallengeManager.md index 9ba7dc204e..2da57c6f0a 100644 --- a/docs/proving/ChallengeManager.md +++ b/docs/proving/ChallengeManager.md @@ -3,7 +3,7 @@ The `ChallengeManager` arbitrates challenge games. Here's a diagram of the challenge state machine: ```mermaid -flowchart LR +flowchart TD B[Block challenge] E[Execution challenge] W[Waiting for timeout to allow for an emergency upgrade] From 74fd846d2515d98d3695d466c870383dae2b134b Mon Sep 17 00:00:00 2001 From: Harry Kalodner Date: Wed, 23 Feb 2022 02:10:36 -0500 Subject: [PATCH 109/110] Update go-ethereum --- go-ethereum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go-ethereum b/go-ethereum index c074ce0b9f..f31341b3df 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit c074ce0b9f64c4123572a158cb4b4abea3ca0666 +Subproject commit f31341b3dfa987719b012bc976a6f4fe3b8a1221 From 1c1cb959a2246bdbdff68cae0ec7132e437c6fc4 Mon Sep 17 00:00:00 2001 From: Chris Buckland Date: Wed, 23 Feb 2022 16:44:50 +0100 Subject: [PATCH 110/110] Removed reference to blockchallenge --- .../sequencerInboxForceInclude.spec.ts | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/solgen/test/contract/sequencerInboxForceInclude.spec.ts b/solgen/test/contract/sequencerInboxForceInclude.spec.ts index 25097534b7..e15536e3cf 100644 --- a/solgen/test/contract/sequencerInboxForceInclude.spec.ts +++ b/solgen/test/contract/sequencerInboxForceInclude.spec.ts @@ -21,20 +21,11 @@ import { BigNumber } from '@ethersproject/bignumber' import { Block, TransactionReceipt } from '@ethersproject/providers' import { expect } from 'chai' import { - BlockChallengeFactory, - BlockChallengeFactory__factory, Bridge, - BridgeCreator__factory, Bridge__factory, - ExecutionChallengeFactory__factory, - ExecutionChallenge__factory, Inbox, Inbox__factory, MessageTester, - OneStepProofEntry__factory, - RollupAdminLogic__factory, - RollupCreator__factory, - RollupUserLogic__factory, SequencerInbox, SequencerInbox__factory, TransparentUpgradeableProxy__factory, @@ -46,14 +37,7 @@ import { BridgeInterface, MessageDeliveredEvent, } from '../../build/types/Bridge' -import { constants, Signer } from 'ethers' -import { RollupCreatedEvent } from '../../build/types/RollupCreator' -import { - hexlify, - keccak256, - parseEther, - solidityKeccak256, -} from 'ethers/lib/utils' +import { Signer } from 'ethers' const mineBlocks = async (count: number, timeDiffPerBlock = 14) => { const block = (await network.provider.send('eth_getBlockByNumber', [