Skip to content

Commit

Permalink
chore(solver): devnet e2e solving (#2560)
Browse files Browse the repository at this point in the history
Solving improvements and refactors and wiring with e2e

issue: none
  • Loading branch information
corverroos authored Nov 26, 2024
1 parent 780efe7 commit 4b5d0ba
Show file tree
Hide file tree
Showing 28 changed files with 357 additions and 199 deletions.
2 changes: 1 addition & 1 deletion cli/cmd/register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func setup(t *testing.T) (context.Context, *ethbackend.Backend, Contracts, EOAS)

ctx := context.Background()

ethCl, _, stop, err := anvil.Start(ctx, tutil.TempDir(t), chainID)
ethCl, stop, err := anvil.Start(ctx, tutil.TempDir(t), chainID)
require.NoError(t, err)
t.Cleanup(stop)

Expand Down
9 changes: 7 additions & 2 deletions e2e/app/anvil.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ func fundAnvil(ctx context.Context, def Definition) error {
return nil
}

toFund := eoa.MustAddresses(netconf.Devnet, eoa.AllRoles()...)
toFund := eoa.MustAddresses(netconf.Devnet, eoa.AllRolesWithSolver()...)
amt := math.NewInt(1000000).MulRaw(1e18).BigInt() // 1M ETH

for _, chain := range def.Testnet.AnvilChains {
if err := anvil.FundAccounts(ctx, chain.ExternalRPC, amt, toFund...); err != nil {
backend, err := def.Backends().Backend(chain.Chain.ChainID)
if err != nil {
return errors.Wrap(err, "get backend")
}

if err := anvil.FundAccounts(ctx, backend.Client, amt, toFund...); err != nil {
return errors.Wrap(err, "fund anvil account")
}
}
Expand Down
7 changes: 6 additions & 1 deletion e2e/app/eoa/eoa.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const (
RoleTester Role = "tester"
// RoleXCaller is used for testing xcalls on a network.
RoleXCaller Role = "xcaller"
// RolveSolver is the allowed solver for solve inbox / outboxes.
// RoleSolver is solver EOA on all networks that interacts with solve inbox / outboxes.
RoleSolver Role = "solver"
)

Expand All @@ -63,6 +63,11 @@ func AllRoles() []Role {
}
}

// TODO(corver): Remove this once solver added above.
func AllRolesWithSolver() []Role {
return append(AllRoles(), RoleSolver)
}

func (r Role) Verify() error {
for _, role := range AllRoles() {
if r == role {
Expand Down
11 changes: 7 additions & 4 deletions e2e/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func Deploy(ctx context.Context, def Definition, cfg DeployConfig) (*pingpong.XD
if err := waitForEVMs(ctx, def.Testnet.EVMChains(), def.Backends()); err != nil {
return nil, err
}
logRPCs(ctx, def)

contracts.UseStagingOmniRPC(def.Testnet.BroadcastOmniEVM().ExternalRPC)

Expand All @@ -98,7 +99,12 @@ func Deploy(ctx context.Context, def Definition, cfg DeployConfig) (*pingpong.XD
return nil, errors.Wrap(err, "deploy portals")
}

logRPCs(ctx, def)
if def.Manifest.DeploySolve {
// Deploy solver before initPortalRegistry, so solver detects boxes after netconf.Await
if err := solve.DeployContracts(ctx, NetworkFromDef(def), def.Backends()); err != nil {
return nil, errors.Wrap(err, "deploy solve")
}
}

// Deploy other contracts (and other on-chain setup)
var eg2 errgroup.Group
Expand All @@ -108,9 +114,6 @@ func Deploy(ctx context.Context, def Definition, cfg DeployConfig) (*pingpong.XD
eg2.Go(func() error { return DeployBridge(ctx, def) })
eg2.Go(func() error { return maybeSubmitNetworkUpgrade(ctx, def) })
eg2.Go(func() error { return FundValidatorsForTesting(ctx, def) })
if def.Manifest.DeploySolve {
eg2.Go(func() error { return solve.DeployContracts(ctx, NetworkFromDef(def), def.Backends()) })
}
if err := eg2.Wait(); err != nil {
return nil, errors.Wrap(err, "deploy other contracts")
}
Expand Down
1 change: 1 addition & 0 deletions e2e/manifests/devnet1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ anvil_chains = ["mock_l1", "mock_l2"]

multi_omni_evms = true
prometheus = true
deploy_solve = true

[node.validator01]
[node.validator02]
Expand Down
54 changes: 35 additions & 19 deletions e2e/solve/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/omni-network/omni/lib/ethclient/ethbackend"
"github.com/omni-network/omni/lib/log"
"github.com/omni-network/omni/lib/netconf"

"golang.org/x/sync/errgroup"
)

// DeployContracts deploys solve inbox / outbox contracts, and devnet app (if devnet).
Expand All @@ -21,38 +23,52 @@ func DeployContracts(ctx context.Context, network netconf.Network, backends ethb

log.Info(ctx, "Deploying solve contracts")

if err := deployBoxes(ctx, network, backends); err != nil {
return errors.Wrap(err, "deploy boxes")
}
var eg errgroup.Group

eg.Go(func() error {
return deployBoxes(ctx, network, backends)
})
eg.Go(func() error {
return devapp.Deploy(ctx, network, backends)
})

if err := devapp.Deploy(ctx, network, backends); err != nil {
return errors.Wrap(err, "deploy devapp")
if err := eg.Wait(); err != nil {
return errors.Wrap(err, "deploy solver contracts")
}

return nil
}

// DeployContracts deploys solve inbox / outbox contracts.
func deployBoxes(ctx context.Context, network netconf.Network, backends ethbackend.Backends) error {
var eg errgroup.Group
for _, chain := range network.EVMChains() {
backend, err := backends.Backend(chain.ID)
if err != nil {
return errors.Wrap(err, "get backend", "chain", chain.Name)
}
eg.Go(func() error {
backend, err := backends.Backend(chain.ID)
if err != nil {
return errors.Wrap(err, "get backend", "chain", chain.Name)
}

addr, _, err := solveinbox.DeployIfNeeded(ctx, network.ID, backend)
if err != nil {
return errors.Wrap(err, "deploy solve inbox")
}
addr, _, err := solveinbox.DeployIfNeeded(ctx, network.ID, backend)
if err != nil {
return errors.Wrap(err, "deploy solve inbox")
}

log.Info(ctx, "SolveInbox deployed", "addr", addr.Hex(), "chain", chain.Name)
log.Debug(ctx, "SolveInbox deployed", "addr", addr.Hex(), "chain", chain.Name)

addr, _, err = solveoutbox.DeployIfNeeded(ctx, network.ID, backend)
if err != nil {
return errors.Wrap(err, "deploy solve outbox")
}
addr, _, err = solveoutbox.DeployIfNeeded(ctx, network.ID, backend)
if err != nil {
return errors.Wrap(err, "deploy solve outbox")
}

log.Debug(ctx, "SolveOutbox deployed", "addr", addr.Hex(), "chain", chain.Name)

return nil
})
}

log.Info(ctx, "SolveOutbox deployed", "addr", addr.Hex(), "chain", chain.Name)
if err := eg.Wait(); err != nil {
return errors.Wrap(err, "deploy solver boxes")
}

return nil
Expand Down
62 changes: 24 additions & 38 deletions e2e/solve/devapp/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/omni-network/omni/lib/errors"
"github.com/omni-network/omni/lib/ethclient/ethbackend"
"github.com/omni-network/omni/lib/netconf"
"github.com/omni-network/omni/lib/xchain"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -24,28 +23,23 @@ type DepositReq struct {
Deposit DepositArgs // deposit args
}

func RequestDeposits(ctx context.Context, endpoints xchain.RPCEndpoints, backends ethbackend.Backends) ([]DepositReq, error) {
func RequestDeposits(ctx context.Context, backends ethbackend.Backends) ([]DepositReq, error) {
app := GetApp()

backend, err := backends.Backend(app.L2.ChainID)
if err != nil {
return nil, err
}

rpc, err := endpoints.ByNameOrID(app.L2.Name, app.L2.ChainID)
if err != nil {
return nil, err
}
const numDeposits = 3

const numDeposits = 10

depositors, err := makeDepositors(numDeposits, backend)
depositors, err := addRandomDepositors(numDeposits, backend)
if err != nil {
return nil, errors.Wrap(err, "make depositors")
}

// fund for gas
if err := anvil.FundAccounts(ctx, rpc, big.NewInt(1e18), depositors...); err != nil {
if err := anvil.FundAccounts(ctx, backend, big.NewInt(1e18), depositors...); err != nil {
return nil, errors.Wrap(err, "fund accounts")
}

Expand All @@ -62,43 +56,36 @@ func RequestDeposits(ctx context.Context, endpoints xchain.RPCEndpoints, backend
return reqs, nil
}

func CheckDeposits(ctx context.Context, backends ethbackend.Backends, reqs []DepositReq) error {
func IsDeposited(ctx context.Context, backends ethbackend.Backends, req DepositReq) (bool, error) {
app := GetApp()

backend, err := backends.Backend(app.L1.ChainID)
if err != nil {
return errors.Wrap(err, "backend")
return false, errors.Wrap(err, "backend")
}

vault, err := bindings.NewMockVault(app.L1Vault, backend)
if err != nil {
return errors.Wrap(err, "new mock vault")
return false, errors.Wrap(err, "new mock vault")
}

callOpts := &bind.CallOpts{Context: ctx}

for _, req := range reqs {
balance, err := vault.Balances(callOpts, req.Deposit.OnBehalfOf)
if err != nil {
return errors.Wrap(err, "get balance")
}

// assumes balance(onBehalfOf) was zero before deposit request
// assumes one deposit per test case onBehalfOf addr
if balance.Cmp(req.Deposit.Amount) != 0 {
return errors.New("missing deposit",
"requester", req.Deposit.OnBehalfOf,
"expected", req.Deposit.Amount,
"actual", balance)
}
balance, err := vault.Balances(callOpts, req.Deposit.OnBehalfOf)
if err != nil {
return false, errors.Wrap(err, "get balance")
}

return nil
// assumes balance(onBehalfOf) was zero before deposit request
// assumes one deposit per test case onBehalfOf addr
return balance.Cmp(req.Deposit.Amount) != 0, nil
}

func makeDepositors(n int, backend *ethbackend.Backend) ([]common.Address, error) {
depositors := make([]common.Address, n)
for i := 0; i < n; i++ {
// addRandomDepositors adds n random depositors privkeys to the backend.
// It returns the addresses of the added depositors.
func addRandomDepositors(n int, backend *ethbackend.Backend) ([]common.Address, error) {
var depositors []common.Address
for range n {
pk, err := crypto.GenerateKey()
if err != nil {
return nil, errors.Wrap(err, "generate key")
Expand All @@ -109,17 +96,15 @@ func makeDepositors(n int, backend *ethbackend.Backend) ([]common.Address, error
return nil, errors.Wrap(err, "add account")
}

depositors[i] = depositor
depositors = append(depositors, depositor)
}

return depositors, nil
}

func requestDeposits(ctx context.Context, backend *ethbackend.Backend, inbox common.Address, depositors []common.Address) ([]DepositReq, error) {
reqs := make([]DepositReq, 0, len(depositors))
for i := 0; i < len(depositors); i++ {
depositor := depositors[i]

var reqs []DepositReq
for _, depositor := range depositors {
deposit := DepositArgs{
OnBehalfOf: depositor,
Amount: big.NewInt(1e18),
Expand Down Expand Up @@ -162,6 +147,7 @@ func requestAtInbox(ctx context.Context, backend *ethbackend.Backend, addr commo
bindings.SolveCall{
DestChainId: app.L1.ChainID,
Target: app.L1Vault,
Value: new(big.Int), // 0 native
Data: data,
},
[]bindings.SolveTokenDeposit{{
Expand Down Expand Up @@ -238,9 +224,9 @@ func parseReqID(inbox bindings.SolveInboxFilterer, logs []*types.Log) ([32]byte,
}

func packDeposit(args DepositArgs) ([]byte, error) {
data, err := vaultDeposit.Inputs.Pack(args)
data, err := vaultDeposit.Inputs.Pack(args.OnBehalfOf, args.Amount)
if err != nil {
return nil, errors.Wrap(err, "unpack data")
return nil, errors.Wrap(err, "pack data")
}

return data, nil
Expand Down
42 changes: 12 additions & 30 deletions e2e/solve/devapp/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,61 +22,43 @@ func (App) ChainID() uint64 {
return evmchain.IDMockL1
}

func (t App) Address() common.Address {
return t.L1Vault
func (a App) Address() common.Address {
return a.L1Vault
}

func (t App) IsAllowedCall(call bindings.SolveCall) bool {
if call.DestChainId != t.ChainID() {
return false
}

if call.Target != t.Address() {
return false
}

_, err := unpackDeposit(call.Data)

return err == nil
}

func (t App) TokenPrereqs(call bindings.SolveCall) ([]bindings.SolveTokenPrereq, error) {
if !t.IsAllowedCall(call) {
return nil, errors.New("call not allowed")
}

func (a App) TokenPrereqs(call bindings.SolveCall) ([]bindings.SolveTokenPrereq, error) {
args, err := unpackDeposit(call.Data)
if err != nil {
return nil, errors.Wrap(err, "unpack deposit")
}

return []bindings.SolveTokenPrereq{
{
Token: t.L1Token,
Amount: args.Amount,
Token: a.L1Token,
Spender: a.L1Vault,
Amount: args.Amount,
},
}, nil
}

func (t App) Verify(srcChainID uint64, call bindings.SolveCall, deposits []bindings.SolveDeposit) error {
func (a App) Verify(srcChainID uint64, call bindings.SolveCall, deposits []bindings.SolveDeposit) error {
// we only accept deposits from mock L2
if srcChainID != evmchain.IDMockL2 {
return errors.New("source chain not supported", "src", srcChainID)
}

if !t.IsAllowedCall(call) {
return errors.New("call not allowed")
}

args, err := unpackDeposit(call.Data)
if err != nil {
return errors.Wrap(err, "invalid deposit")
}

var l2token *bindings.SolveDeposit
if _, err := a.TokenPrereqs(call); err != nil {
return errors.Wrap(err, "token prereqs")
}

var l2token *bindings.SolveDeposit
for _, deposit := range deposits {
if deposit.Token == t.L2Token {
if deposit.Token == a.L2Token {
l2token = &deposit
}
}
Expand Down
Loading

0 comments on commit 4b5d0ba

Please sign in to comment.