Skip to content

Commit

Permalink
Merge pull request #1875 from OffchainLabs/lazy-validator-wallet
Browse files Browse the repository at this point in the history
Create validator wallet lazily
  • Loading branch information
hkalodner authored Dec 9, 2021
2 parents 906cd1e + 3919b89 commit 2ad1d6c
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 74 deletions.
14 changes: 7 additions & 7 deletions packages/arb-node-core/challenge/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ func executeChallenge(
seqInbox, err := ethbridge.NewSequencerInboxWatcher(seqInboxAddr, client)
test.FailIfError(t, err)

challenger := NewChallenger(challengerChallengeCon, seqInbox, correctLookup, challengedAssertion, challengerWallet.Address())
asserter := NewChallenger(asserterChallengeCon, seqInbox, falseLookup, challengedAssertion, asserterWallet.Address())
challenger := NewChallenger(challengerChallengeCon, seqInbox, correctLookup, challengedAssertion, common.NewAddressFromEth(*challengerWallet.Address()))
asserter := NewChallenger(asserterChallengeCon, seqInbox, falseLookup, challengedAssertion, common.NewAddressFromEth(*asserterWallet.Address()))

var moves []Move

Expand Down Expand Up @@ -124,7 +124,7 @@ func executeChallenge(
checkTurn(t, challenge, turn)
}

checkChallengeCompleted(t, tester, challengerWallet.Address().ToEthAddress(), asserterWallet.Address().ToEthAddress())
checkChallengeCompleted(t, tester, *challengerWallet.Address(), *asserterWallet.Address())
return moves, nil
}

Expand Down Expand Up @@ -304,21 +304,21 @@ func initializeChallengeTest(

asserterAuth, err := transactauth.NewTransactAuth(ctx, client, asserter)
test.FailIfError(t, err)
asserterWallet, err := ethbridge.NewValidator(asserterWalletAddress, ethcommon.Address{}, client, asserterAuth)
asserterWallet, err := ethbridge.NewValidator(&asserterWalletAddress, ethcommon.Address{}, ethcommon.Address{}, client, asserterAuth, 0, nil)
test.FailIfError(t, err)

challengerAuth, err := transactauth.NewTransactAuth(ctx, client, challenger)
test.FailIfError(t, err)
challengerWallet, err := ethbridge.NewValidator(challengerWalletAddress, ethcommon.Address{}, client, challengerAuth)
challengerWallet, err := ethbridge.NewValidator(&challengerWalletAddress, ethcommon.Address{}, ethcommon.Address{}, client, challengerAuth, 0, nil)
test.FailIfError(t, err)

startChallenge := func(assertion *core.Assertion) {
_, err = tester.StartChallenge(
deployer,
assertion.ExecutionHash(),
assertion.After.TotalMessagesRead,
asserterWallet.Address().ToEthAddress(),
challengerWallet.Address().ToEthAddress(),
*asserterWallet.Address(),
*challengerWallet.Address(),
asserterTime,
challengerTime,
sequencerBridgeAddr,
Expand Down
38 changes: 11 additions & 27 deletions packages/arb-node-core/cmd/arb-validator/arb-validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"os"
"path"
"strings"
"time"

"github.com/pkg/errors"
"github.com/rs/zerolog"
Expand Down Expand Up @@ -177,34 +176,19 @@ func startup() error {
if err != nil {
return errors.Wrap(err, "error creating connecting to chain")
}
validatorAddress := ethcommon.Address{}
if chainState.ValidatorWallet == "" {
for {
validatorAddress, err = ethbridge.CreateValidatorWallet(ctx, validatorWalletFactoryAddr, config.Rollup.FromBlock, valAuth, l1Client)
if err == nil {
break
}
logger.Warn().Err(err).
Str("sender", auth.From.Hex()).
Msg("Failed to deploy validator wallet")

select {
case <-ctx.Done():
return nil
case <-time.After(time.Second * 5):
}
}
chainState.ValidatorWallet = validatorAddress.String()

var validatorAddress *ethcommon.Address
if chainState.ValidatorWallet != "" {
addr := ethcommon.HexToAddress(chainState.ValidatorWallet)
validatorAddress = &addr
}
onValidatorWalletCreated := func(addr ethcommon.Address) {
chainState.ValidatorWallet = addr.String()
newChainStateData, err := json.Marshal(chainState)
if err != nil {
return errors.Wrap(err, "failed to marshal chain state")
log.Warn().Err(err).Msg("failed to marshal chain state")
} else if err := ioutil.WriteFile(chainStatePath, newChainStateData, 0644); err != nil {
log.Warn().Err(err).Msg("failed to write chain state config")
}
if err := ioutil.WriteFile(chainStatePath, newChainStateData, 0644); err != nil {
return errors.Wrap(err, "failed to write chain state config")
}
} else {
validatorAddress = ethcommon.HexToAddress(chainState.ValidatorWallet)
}

mon, err := monitor.NewMonitor(config.GetValidatorDatabasePath(), config.Rollup.Machine.Filename, &config.Core)
Expand All @@ -213,7 +197,7 @@ func startup() error {
}
defer mon.Close()

val, err := ethbridge.NewValidator(validatorAddress, rollupAddr, l1Client, valAuth)
val, err := ethbridge.NewValidator(validatorAddress, validatorWalletFactoryAddr, rollupAddr, l1Client, valAuth, config.Rollup.FromBlock, onValidatorWalletCreated)
if err != nil {
return errors.Wrap(err, "error creating validator wallet")
}
Expand Down
9 changes: 6 additions & 3 deletions packages/arb-node-core/ethbridge/builderbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type BuilderBackend struct {
transactions []*types.Transaction
builderAuth *bind.TransactOpts
realSender common.Address
wallet common.Address
wallet *common.Address

realClient ethutils.EthClient
}
Expand All @@ -51,7 +51,7 @@ func NewBuilderBackend(wallet *ValidatorWallet) (*BuilderBackend, error) {
return &BuilderBackend{
builderAuth: fakeAuth,
realSender: wallet.From().ToEthAddress(),
wallet: wallet.Address().ToEthAddress(),
wallet: wallet.Address(),
realClient: wallet.client,
}, nil
}
Expand Down Expand Up @@ -103,13 +103,16 @@ func (b *BuilderBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg)
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,
To: b.wallet,
Value: totalAmount,
Data: realData,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/arb-node-core/ethbridge/rollup.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ func NewRollup(address ethcommon.Address, fromBlock int64, client ethutils.EthCl
}, nil
}

func (r *Rollup) RejectNextNode(ctx context.Context, staker common.Address) error {
_, err := r.builderCon.RejectNextNode(authWithContext(ctx, r.builderAuth), staker.ToEthAddress())
func (r *Rollup) RejectNextNode(ctx context.Context, staker ethcommon.Address) error {
_, err := r.builderCon.RejectNextNode(authWithContext(ctx, r.builderAuth), staker)
return errors.WithStack(err)
}

Expand Down
71 changes: 55 additions & 16 deletions packages/arb-node-core/ethbridge/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,40 @@ func init() {
}

type ValidatorWallet struct {
con *ethbridgecontracts.Validator
address ethcommon.Address
client ethutils.EthClient
auth transactauth.TransactAuth
rollupAddress ethcommon.Address
con *ethbridgecontracts.Validator
address *ethcommon.Address
onWalletCreated func(ethcommon.Address)
client ethutils.EthClient
auth transactauth.TransactAuth
rollupAddress ethcommon.Address
walletFactoryAddr ethcommon.Address
rollupFromBlock int64
}

func NewValidator(address, rollupAddress ethcommon.Address, client ethutils.EthClient, auth transactauth.TransactAuth) (*ValidatorWallet, error) {
con, err := ethbridgecontracts.NewValidator(address, client)
if err != nil {
return nil, err
func NewValidator(address *ethcommon.Address, walletFactoryAddr, rollupAddress ethcommon.Address, client ethutils.EthClient, auth transactauth.TransactAuth, rollupFromBlock int64, onWalletCreated func(ethcommon.Address)) (*ValidatorWallet, error) {
var con *ethbridgecontracts.Validator
if address != nil {
var err error
con, err = ethbridgecontracts.NewValidator(*address, client)
if err != nil {
return nil, err
}
}
return &ValidatorWallet{
con: con,
address: address,
client: client,
auth: auth,
rollupAddress: rollupAddress,
con: con,
address: address,
onWalletCreated: onWalletCreated,
client: client,
auth: auth,
rollupAddress: rollupAddress,
walletFactoryAddr: walletFactoryAddr,
rollupFromBlock: rollupFromBlock,
}, nil
}

func (v *ValidatorWallet) Address() common.Address {
return common.NewAddressFromEth(v.address)
// May be the nil if the wallet hasn't been deployed yet
func (v *ValidatorWallet) Address() *ethcommon.Address {
return v.address
}

func (v *ValidatorWallet) From() common.Address {
Expand All @@ -93,6 +104,28 @@ func (v *ValidatorWallet) executeTransaction(ctx context.Context, tx *types.Tran
})
}

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 := ethbridgecontracts.NewValidator(*v.address, v.client)
if err != nil {
return err
}
v.con = con
return nil
}

func combineTxes(txes []*types.Transaction) ([][]byte, []ethcommon.Address, []*big.Int, *big.Int) {
totalAmount := big.NewInt(0)
data := make([][]byte, 0, len(txes))
Expand All @@ -108,6 +141,7 @@ func combineTxes(txes []*types.Transaction) ([][]byte, []ethcommon.Address, []*b
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) (*arbtransaction.ArbTransaction, error) {
txes := builder.transactions
if len(txes) == 0 {
Expand Down Expand Up @@ -135,6 +169,11 @@ 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
}

arbTx, err := transactauth.MakeTx(ctx, v.auth, func(auth *bind.TransactOpts) (*types.Transaction, error) {
auth.Value = totalAmount
return v.con.ExecuteTransactions(auth, data, dest, amount)
Expand Down
44 changes: 31 additions & 13 deletions packages/arb-node-core/staker/staker.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,22 @@ func (s *Staker) Act(ctx context.Context) (*arbtransaction.ArbTransaction, error
return nil, nil
}
s.builder.ClearTransactions()
rawInfo, err := s.rollup.StakerInfo(ctx, s.wallet.Address())
if err != nil {
return nil, err
var rawInfo *ethbridge.StakerInfo
walletAddress := s.wallet.Address()
var walletAddressOrZero common.Address
if walletAddress != nil {
walletAddressOrZero = common.NewAddressFromEth(*walletAddress)
}
if walletAddress != nil {
var err error
rawInfo, err = s.rollup.StakerInfo(ctx, walletAddressOrZero)
if err != nil {
return nil, err
}
}
latestStakedNode, latestStakedNodeHash, err := s.validatorUtils.LatestStaked(ctx, s.wallet.Address())
// 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
}
Expand Down Expand Up @@ -306,20 +317,25 @@ func (s *Staker) handleConflict(ctx context.Context, info *ethbridge.StakerInfo)
return err
}

s.activeChallenge = challenge.NewChallenger(challengeCon, s.sequencerInbox, s.lookup, nodeInfo.Assertion, s.wallet.Address())
// This is safe to dereference, as handleConflict can only be called if we have a wallet address
ourAddr := common.NewAddressFromEth(*s.wallet.Address())
s.activeChallenge = challenge.NewChallenger(challengeCon, s.sequencerInbox, s.lookup, nodeInfo.Assertion, ourAddr)
}

_, err := s.activeChallenge.HandleConflict(ctx)
return err
}

func (s *Staker) newStake(ctx context.Context) error {
info, err := s.rollup.StakerInfo(ctx, s.wallet.Address())
if err != nil {
return err
}
if info != nil {
return nil
var addr = s.wallet.Address()
if addr != nil {
info, err := s.rollup.StakerInfo(ctx, common.NewAddressFromEth(*addr))
if err != nil {
return err
}
if info != nil {
return nil
}
}
stakeAmount, err := s.rollup.CurrentRequiredStake(ctx)
if err != nil {
Expand Down Expand Up @@ -374,6 +390,8 @@ func (s *Staker) createConflict(ctx context.Context, info *ethbridge.StakerInfo)
if err != nil {
return err
}
// Safe to dereference as createConflict is only called when we have a wallet address
walletAddr := common.NewAddressFromEth(*s.wallet.Address())
for _, staker := range stakers {
stakerInfo, err := s.rollup.StakerInfo(ctx, staker)
if err != nil {
Expand All @@ -382,14 +400,14 @@ func (s *Staker) createConflict(ctx context.Context, info *ethbridge.StakerInfo)
if stakerInfo.CurrentChallenge != nil {
continue
}
conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(ctx, s.wallet.Address(), staker)
conflictType, node1, node2, err := s.validatorUtils.FindStakerConflict(ctx, walletAddr, staker)
if err != nil {
return err
}
if conflictType != ethbridge.CONFLICT_TYPE_FOUND {
continue
}
staker1 := s.wallet.Address()
staker1 := walletAddr
staker2 := staker
if node2.Cmp(node1) < 0 {
staker1, staker2 = staker2, staker1
Expand Down
4 changes: 2 additions & 2 deletions packages/arb-node-core/staker/staker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ func runStakersTest(t *testing.T, faultConfig challenge.FaultConfig, maxGasPerNo
mon, shutdown := monitor.PrepareArbCore(t)
defer shutdown()

val, err := ethbridge.NewValidator(validatorAddress, rollupAddr, client, valAuth)
val, err := ethbridge.NewValidator(nil, validatorWalletFactory, rollupAddr, client, valAuth, 0, nil)
test.FailIfError(t, err)
val2, err := ethbridge.NewValidator(validatorAddress2, rollupAddr, client, val2Auth)
val2, err := ethbridge.NewValidator(nil, validatorWalletFactory, rollupAddr, client, val2Auth, 0, nil)
test.FailIfError(t, err)

staker, _, err := NewStaker(ctx, mon.Core, client, val, rollupBlock.Int64(), common.NewAddressFromEth(validatorUtilsAddr), MakeNodesStrategy, bind.CallOpts{}, valAuth, configuration.Validator{})
Expand Down
10 changes: 6 additions & 4 deletions packages/arb-node-core/staker/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ func (v *Validator) removeOldStakers(ctx context.Context, dontRemoveSelf bool) (
if err != nil {
return nil, err
}
if dontRemoveSelf {
walletAddr := v.wallet.Address()
if dontRemoveSelf && walletAddr != nil {
for i, staker := range stakersToEliminate {
if staker == v.wallet.Address() {
if staker.ToEthAddress() == *walletAddr {
stakersToEliminate[i] = stakersToEliminate[len(stakersToEliminate)-1]
stakersToEliminate = stakersToEliminate[:len(stakersToEliminate)-1]
break
Expand Down Expand Up @@ -136,12 +137,13 @@ func (v *Validator) resolveNextNode(ctx context.Context, info *ethbridge.StakerI
}
switch confirmType {
case ethbridge.CONFIRM_TYPE_INVALID:
if info == nil || info.LatestStakedNode.Cmp(unresolvedNodeIndex) <= 0 {
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, v.wallet.Address())
return v.rollup.RejectNextNode(ctx, *addr)
case ethbridge.CONFIRM_TYPE_VALID:
nodeInfo, err := v.rollup.RollupWatcher.LookupNode(ctx, unresolvedNodeIndex)
if err != nil {
Expand Down

0 comments on commit 2ad1d6c

Please sign in to comment.