Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable dataposter for validator contract wallet #1788

Merged
merged 27 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6f3b14b
Enable dataposter for ContractValidatorWallet
anodar Jul 31, 2023
0c78d7f
Merge branch 'master' into dataposter-for-validator-contract-wallet
anodar Aug 2, 2023
244a481
Set correct l1reader in staker test, allow slicestorage skipping indexes
anodar Aug 2, 2023
c5c8db2
Pass correct gaslimit to dataposter from validator wallet
anodar Aug 3, 2023
3cf95da
Post gas refunder transactions through dataposter
anodar Aug 3, 2023
8c80c4e
Pass 0 gaslimit to dataposter so that it always uses gas estimation
anodar Aug 3, 2023
e3d2975
Revert "Pass correct gaslimit to dataposter from validator wallet"
anodar Aug 3, 2023
81ed4be
Revert "Pass 0 gaslimit to dataposter so that it always uses gas esti…
anodar Aug 3, 2023
37b32f0
Revert "Pass 0 gaslimit to dataposter so that it always uses gas esti…
anodar Aug 3, 2023
cac1373
Merge branch 'dataposter-for-validator-contract-wallet' of github.com…
anodar Aug 3, 2023
4f2200b
Merge commit 'refs/pull/1793/head' of github.com:OffchainLabs/nitro i…
anodar Aug 3, 2023
11a8b0c
Add gas estimation to ContractValidatorWallet
anodar Aug 3, 2023
970d60e
Drop blockscout
anodar Aug 3, 2023
38abb75
Revert enabling staker_challenge_test.go for -race runs
anodar Aug 3, 2023
5768223
Add option to dataposter to not store the state
anodar Aug 4, 2023
323b7a7
Merge branch 'dataposter-for-validator' of github.com:OffchainLabs/ni…
anodar Aug 8, 2023
689a607
Take into account nil values of queuedTransaction in Equals method
anodar Aug 8, 2023
426e07d
Merge branch 'master' of github.com:OffchainLabs/nitro into dataposte…
anodar Aug 15, 2023
eab4daf
Merge branch 'dataposter-for-validator-contract-wallet' into batchpos…
anodar Aug 15, 2023
3b7524f
Merge pull request #1794 from OffchainLabs/batchposter-disable-option
anodar Aug 15, 2023
66aa275
merge branch master
anodar Aug 15, 2023
a37de92
Merge branch 'dataposter-for-validator-contract-wallet' of github.com…
anodar Aug 15, 2023
0e6b2ef
drop coverage.txt that was accidentally added to the pr
anodar Aug 15, 2023
2ad55ec
allow out-of-bound index in slicestorage
anodar Aug 15, 2023
ae0b12a
encode empty elements in slicestorage
anodar Aug 15, 2023
68751b0
Do not allow out-of-bound indexes in slicestorage, use dataposter for…
anodar Aug 15, 2023
d1a4f70
drop debugging log, drop redundant gasprice in gas estimation
anodar Aug 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions arbnode/dataposter/data_poster.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/go-redis/redis/v8"
"github.com/offchainlabs/nitro/arbnode/dataposter/leveldb"
"github.com/offchainlabs/nitro/arbnode/dataposter/noop"
"github.com/offchainlabs/nitro/arbnode/dataposter/slice"
"github.com/offchainlabs/nitro/arbnode/dataposter/storage"
"github.com/offchainlabs/nitro/arbutil"
Expand Down Expand Up @@ -97,8 +98,10 @@ func NewDataPoster(db ethdb.Database, headerReader *headerreader.HeaderReader, a
}
var queue QueueStorage
switch {
case config().EnableLevelDB:
case config().UseLevelDB:
queue = leveldb.New(db)
case config().UseNoOpStorage:
queue = &noop.Storage{}
case redisClient == nil:
queue = slice.NewStorage()
default:
Expand Down Expand Up @@ -280,6 +283,7 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim
if err != nil {
return nil, fmt.Errorf("failed to update data poster balance: %w", err)
}

feeCap, tipCap, err := p.feeAndTipCaps(ctx, gasLimit, nil, nil, dataCreatedAt, 0)
if err != nil {
return nil, err
Expand Down Expand Up @@ -560,6 +564,8 @@ type DataPosterConfig struct {
UrgencyGwei float64 `koanf:"urgency-gwei" reload:"hot"`
MinFeeCapGwei float64 `koanf:"min-fee-cap-gwei" reload:"hot"`
MinTipCapGwei float64 `koanf:"min-tip-cap-gwei" reload:"hot"`
UseLevelDB bool `koanf:"use-leveldb" reload:"hot"`
UseNoOpStorage bool `koanf:"use-noop-storage" reload:"hot"`
MaxTipCapGwei float64 `koanf:"max-tip-cap-gwei" reload:"hot"`
EnableLevelDB bool `koanf:"enable-leveldb" reload:"hot"`
}
Expand All @@ -577,7 +583,8 @@ func DataPosterConfigAddOptions(prefix string, f *pflag.FlagSet) {
f.Float64(prefix+".urgency-gwei", DefaultDataPosterConfig.UrgencyGwei, "the urgency to use for maximum fee cap calculation")
f.Float64(prefix+".min-fee-cap-gwei", DefaultDataPosterConfig.MinFeeCapGwei, "the minimum fee cap to post transactions at")
f.Float64(prefix+".min-tip-cap-gwei", DefaultDataPosterConfig.MinTipCapGwei, "the minimum tip cap to post transactions at")
f.Bool(prefix+".enable-leveldb", DefaultDataPosterConfig.EnableLevelDB, "uses leveldb when enabled")
f.Bool(prefix+".use-leveldb", DefaultDataPosterConfig.UseLevelDB, "uses leveldb when enabled")
f.Bool(prefix+".use-noop-storage", DefaultDataPosterConfig.UseLevelDB, "uses noop storage, it doesn't store anything")
signature.SimpleHmacConfigAddOptions(prefix+".redis-signer", f)
}

Expand All @@ -588,6 +595,8 @@ var DefaultDataPosterConfig = DataPosterConfig{
UrgencyGwei: 2.,
MaxMempoolTransactions: 64,
MinTipCapGwei: 0.05,
UseLevelDB: false,
UseNoOpStorage: false,
MaxTipCapGwei: 5,
EnableLevelDB: false,
}
Expand All @@ -600,6 +609,8 @@ var TestDataPosterConfig = DataPosterConfig{
UrgencyGwei: 2.,
MaxMempoolTransactions: 64,
MinTipCapGwei: 0.05,
UseLevelDB: false,
UseNoOpStorage: false,
MaxTipCapGwei: 5,
EnableLevelDB: false,
}
37 changes: 37 additions & 0 deletions arbnode/dataposter/noop/storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2021-2023, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
package noop

import (
"context"

"github.com/offchainlabs/nitro/arbnode/dataposter/storage"
)

// Storage implements noop storage for dataposter. This is for clients that want
// to have option to directly post to geth without keeping state.
type Storage struct{}

func (s *Storage) FetchContents(_ context.Context, _, _ uint64) ([]*storage.QueuedTransaction, error) {
return nil, nil
}

func (s *Storage) FetchLast(ctx context.Context) (*storage.QueuedTransaction, error) {
return nil, nil
}

func (s *Storage) Prune(_ context.Context, _ uint64) error {
return nil
}

func (s *Storage) Put(_ context.Context, _ uint64, _, _ *storage.QueuedTransaction) error {
return nil
}

func (s *Storage) Length(context.Context) (int, error) {
return 0, nil
}

func (s *Storage) IsPersistent() bool {
return false
}
2 changes: 1 addition & 1 deletion arbnode/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ func createNodeImpl(
tmpAddress := common.HexToAddress(config.Staker.ContractWalletAddress)
existingWalletAddress = &tmpAddress
}
wallet, err = staker.NewContractValidatorWallet(existingWalletAddress, deployInfo.ValidatorWalletCreator, deployInfo.Rollup, l1Reader, txOptsValidator, int64(deployInfo.DeployedAt), func(common.Address) {})
wallet, err = staker.NewContractValidatorWallet(dp, existingWalletAddress, deployInfo.ValidatorWalletCreator, deployInfo.Rollup, l1Reader, txOptsValidator, int64(deployInfo.DeployedAt), func(common.Address) {}, config.BlockValidator.ExtraGas)
if err != nil {
return nil, err
}
Expand Down
4 changes: 4 additions & 0 deletions staker/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type BlockValidatorConfig struct {
DataPoster dataposter.DataPosterConfig `koanf:"data-poster" reload:"hot"`
RedisUrl string `koanf:"redis-url"`
RedisLock redislock.SimpleCfg `koanf:"redis-lock" reload:"hot"`
ExtraGas uint64 `koanf:"extra-gas" reload:"hot"`
}

func (c *BlockValidatorConfig) Validate() error {
Expand All @@ -112,6 +113,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) {
f.String(prefix+".current-module-root", DefaultBlockValidatorConfig.CurrentModuleRoot, "current wasm module root ('current' read from chain, 'latest' from machines/latest dir, or provide hash)")
f.String(prefix+".pending-upgrade-module-root", DefaultBlockValidatorConfig.PendingUpgradeModuleRoot, "pending upgrade wasm module root to additionally validate (hash, 'latest' or empty)")
f.Bool(prefix+".failure-is-fatal", DefaultBlockValidatorConfig.FailureIsFatal, "failing a validation is treated as a fatal error")
f.Uint64(prefix+".extra-gas", DefaultBlockValidatorConfig.ExtraGas, "use this much more gas than estimation says is necessary to post transactions")
BlockValidatorDangerousConfigAddOptions(prefix+".dangerous", f)
dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f)
f.String(prefix+".redis-url", DefaultBlockValidatorConfig.RedisUrl, "redis url for block validator")
Expand All @@ -135,6 +137,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{
DataPoster: dataposter.DefaultDataPosterConfig,
RedisUrl: "",
RedisLock: redislock.DefaultCfg,
ExtraGas: 50000,
}

var TestBlockValidatorConfig = BlockValidatorConfig{
Expand All @@ -150,6 +153,7 @@ var TestBlockValidatorConfig = BlockValidatorConfig{
DataPoster: dataposter.TestDataPosterConfig,
RedisUrl: "",
RedisLock: redislock.DefaultCfg,
ExtraGas: 50000,
}

var DefaultBlockValidatorDangerousConfig = BlockValidatorDangerousConfig{
Expand Down
82 changes: 77 additions & 5 deletions staker/validator_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,24 @@ package staker
import (
"context"
"errors"
"fmt"
"math/big"
"strings"
"sync/atomic"
"time"

"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/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/offchainlabs/nitro/arbnode/dataposter"
"github.com/offchainlabs/nitro/arbutil"
"github.com/offchainlabs/nitro/solgen/go/rollupgen"
"github.com/offchainlabs/nitro/util/arbmath"
"github.com/offchainlabs/nitro/util/headerreader"
"github.com/offchainlabs/nitro/util/stopwaiter"
)

Expand Down Expand Up @@ -62,18 +68,21 @@ type ContractValidatorWallet struct {
con *rollupgen.ValidatorWallet
address atomic.Pointer[common.Address]
onWalletCreated func(common.Address)
l1Reader L1ReaderInterface
l1Reader *headerreader.HeaderReader
auth *bind.TransactOpts
walletFactoryAddr common.Address
rollupFromBlock int64
rollup *rollupgen.RollupUserLogic
rollupAddress common.Address
challengeManagerAddress common.Address
dataPoster *dataposter.DataPoster
extraGas uint64
}

var _ ValidatorWalletInterface = (*ContractValidatorWallet)(nil)

func NewContractValidatorWallet(address *common.Address, walletFactoryAddr, rollupAddress common.Address, l1Reader L1ReaderInterface, auth *bind.TransactOpts, rollupFromBlock int64, onWalletCreated func(common.Address)) (*ContractValidatorWallet, error) {
func NewContractValidatorWallet(dp *dataposter.DataPoster, address *common.Address, walletFactoryAddr, rollupAddress common.Address, l1Reader *headerreader.HeaderReader, auth *bind.TransactOpts, rollupFromBlock int64, onWalletCreated func(common.Address),
extraGas uint64) (*ContractValidatorWallet, error) {
var con *rollupgen.ValidatorWallet
if address != nil {
var err error
Expand All @@ -95,6 +104,8 @@ func NewContractValidatorWallet(address *common.Address, walletFactoryAddr, roll
rollupAddress: rollupAddress,
rollup: rollup,
rollupFromBlock: rollupFromBlock,
dataPoster: dp,
extraGas: extraGas,
}
// Go complains if we make an address variable before wallet and copy it in
wallet.address.Store(address)
Expand Down Expand Up @@ -180,7 +191,15 @@ func (v *ContractValidatorWallet) executeTransaction(ctx context.Context, tx *ty
if err != nil {
return nil, err
}
return v.con.ExecuteTransactionWithGasRefunder(auth, gasRefunder, tx.Data(), *tx.To(), tx.Value())
data, err := validatorABI.Pack("executeTransactionWithGasRefunder", gasRefunder, tx.Data(), *tx.To(), tx.Value())
if err != nil {
return nil, fmt.Errorf("packing arguments for executeTransactionWithGasRefunder: %w", err)
}
gas, err := v.gasForTxData(ctx, auth, data)
if err != nil {
return nil, fmt.Errorf("getting gas for tx data: %w", err)
}
return v.dataPoster.PostTransaction(ctx, time.Now(), auth.Nonce.Uint64(), nil, *v.Address(), data, gas, auth.Value)
}

func (v *ContractValidatorWallet) populateWallet(ctx context.Context, createIfMissing bool) error {
Expand Down Expand Up @@ -283,20 +302,73 @@ func (v *ContractValidatorWallet) ExecuteTransactions(ctx context.Context, build
if err != nil {
return nil, err
}
arbTx, err := v.con.ExecuteTransactionsWithGasRefunder(auth, gasRefunder, data, dest, amount)
txData, err := validatorABI.Pack("executeTransactionsWithGasRefunder", gasRefunder, data, dest, amount)
if err != nil {
return nil, fmt.Errorf("packing arguments for executeTransactionWithGasRefunder: %w", err)
}
gas, err := v.gasForTxData(ctx, auth, txData)
if err != nil {
return nil, fmt.Errorf("getting gas for tx data: %w", err)
}
arbTx, err := v.dataPoster.PostTransaction(ctx, time.Now(), auth.Nonce.Uint64(), nil, *v.Address(), txData, gas, auth.Value)
if err != nil {
return nil, err
}
builder.transactions = nil
return arbTx, nil
}

func (v *ContractValidatorWallet) estimateGas(ctx context.Context, value *big.Int, data []byte) (uint64, error) {
h, err := v.l1Reader.LastHeader(ctx)
if err != nil {
return 0, fmt.Errorf("getting the last header: %w", err)
}
gasFeeCap := new(big.Int).Mul(h.BaseFee, big.NewInt(2))
gasFeeCap = arbmath.BigMax(gasFeeCap, arbmath.FloatToBig(params.GWei))

gasTipCap, err := v.l1Reader.Client().SuggestGasTipCap(ctx)
if err != nil {
return 0, fmt.Errorf("getting suggested gas tip cap: %w", err)
}
g, err := v.l1Reader.Client().EstimateGas(
ctx,
ethereum.CallMsg{
From: v.auth.From,
To: v.Address(),
Value: value,
Data: data,
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
},
)
if err != nil {
return 0, fmt.Errorf("estimating gas: %w", err)
}
return g + v.extraGas, nil
}

func (v *ContractValidatorWallet) TimeoutChallenges(ctx context.Context, challenges []uint64) (*types.Transaction, error) {
auth, err := v.getAuth(ctx, nil)
if err != nil {
return nil, err
}
return v.con.TimeoutChallenges(auth, v.challengeManagerAddress, challenges)
data, err := validatorABI.Pack("timeoutChallenges", v.challengeManagerAddress, challenges)
if err != nil {
return nil, fmt.Errorf("packing arguments for timeoutChallenges: %w", err)
}
gas, err := v.gasForTxData(ctx, auth, data)
if err != nil {
return nil, fmt.Errorf("getting gas for tx data: %w", err)
}
return v.dataPoster.PostTransaction(ctx, time.Now(), auth.Nonce.Uint64(), nil, *v.Address(), data, gas, auth.Value)
}

// gasForTxData returns auth.GasLimit if it's nonzero, otherwise returns estimate.
func (v *ContractValidatorWallet) gasForTxData(ctx context.Context, auth *bind.TransactOpts, data []byte) (uint64, error) {
if auth.GasLimit != 0 {
return auth.GasLimit, nil
}
return v.estimateGas(ctx, auth.Value, data)
}

func (v *ContractValidatorWallet) L1Client() arbutil.L1Interface {
Expand Down
17 changes: 12 additions & 5 deletions system_tests/staker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool)

valConfig := staker.L1ValidatorConfig{}

valWalletA, err := staker.NewContractValidatorWallet(nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {})
dpA, err := arbnode.ValidatorDataposter(rawdb.NewTable(l2nodeB.ArbDB, storage.BlockValidatorPrefix), l2nodeA.L1Reader, &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil)
if err != nil {
t.Fatalf("Error creating validator dataposter: %v", err)
}
valWalletA, err := staker.NewContractValidatorWallet(dpA, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, &l1authA, 0, func(common.Address) {}, 10000)
Require(t, err)
if honestStakerInactive {
valConfig.Strategy = "Defensive"
Expand Down Expand Up @@ -174,11 +178,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool)
}
Require(t, err)

dp, err := arbnode.ValidatorDataposter(rawdb.NewTable(l2nodeB.ArbDB, storage.BlockValidatorPrefix), l2nodeB.L1Reader, &l1authB, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil)
dpB, err := arbnode.ValidatorDataposter(rawdb.NewTable(l2nodeB.ArbDB, storage.BlockValidatorPrefix), l2nodeB.L1Reader, &l1authB, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil)
if err != nil {
t.Fatalf("Error creating validator dataposter: %v", err)
}
valWalletB, err := staker.NewEoaValidatorWallet(dp, l2nodeB.DeployInfo.Rollup, l2nodeB.L1Reader.Client(), &l1authB)
valWalletB, err := staker.NewEoaValidatorWallet(dpB, l2nodeB.DeployInfo.Rollup, l2nodeB.L1Reader.Client(), &l1authB)
Require(t, err)
valConfig.Strategy = "MakeNodes"
statelessB, err := staker.NewStatelessBlockValidator(
Expand Down Expand Up @@ -213,8 +217,11 @@ func stakerTestImpl(t *testing.T, faultyStaker bool, honestStakerInactive bool)
err = valWalletB.Initialize(ctx)
Require(t, err)
}

valWalletC, err := staker.NewContractValidatorWallet(nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, nil, 0, func(common.Address) {})
dpC, err := arbnode.ValidatorDataposter(rawdb.NewTable(l2nodeB.ArbDB, storage.BlockValidatorPrefix), l2nodeA.L1Reader, &l1authA, NewFetcherFromConfig(arbnode.ConfigDefaultL1NonSequencerTest()), nil)
if err != nil {
t.Fatalf("Error creating validator dataposter: %v", err)
}
valWalletC, err := staker.NewContractValidatorWallet(dpC, nil, l2nodeA.DeployInfo.ValidatorWalletCreator, l2nodeA.DeployInfo.Rollup, l2nodeA.L1Reader, nil, 0, func(common.Address) {}, 10000)
Require(t, err)
valConfig.Strategy = "Watchtower"
stakerC, err := staker.NewStaker(
Expand Down
Loading