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

[NIT-2628] Add option to enable fast confirmation #2434

Merged
merged 32 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7f5f406
fast confirmation
amsanghi Jun 26, 2024
aaaa297
fast confirmation with safe
amsanghi Jun 27, 2024
ff4b92c
Merge branch 'master' into fast_confirmation
amsanghi Jun 27, 2024
97a3dba
fix makefile
amsanghi Jun 27, 2024
7a675c1
fix docker
amsanghi Jun 27, 2024
32bb178
fix docker
amsanghi Jun 27, 2024
da7f176
fix docker
amsanghi Jun 27, 2024
c0ef5f1
fix docker
amsanghi Jun 27, 2024
c8797fa
fix makefile
amsanghi Jun 27, 2024
a13c087
Merge branch 'master' into fast_confirmation
amsanghi Jul 8, 2024
b668306
fix contracts
amsanghi Jul 8, 2024
f66590c
cherry pick fast confrimation commits
amsanghi Jul 8, 2024
09b4580
Merge branch 'master' into fast_confirmation
amsanghi Jul 8, 2024
ca9d869
fix docker
amsanghi Jul 8, 2024
5e28815
fix
amsanghi Jul 8, 2024
a736fd8
fix
amsanghi Jul 8, 2024
89227b1
update test
amsanghi Jul 9, 2024
fd2a1ba
Merge branch 'master' into fast_confirmation
amsanghi Jul 9, 2024
08320f6
pin safe module to 192c7dc
amsanghi Jul 9, 2024
bc279c2
fix ci
amsanghi Jul 9, 2024
29b51d2
fix
amsanghi Jul 9, 2024
4f73761
Merge branch 'master' into fast_confirmation
amsanghi Jul 11, 2024
b5690fe
Merge branch 'master' into fast_confirmation
amsanghi Jul 19, 2024
95981b6
Changes based on PR comments
amsanghi Jul 19, 2024
ca4b1d5
Changes based on PR comments
amsanghi Jul 19, 2024
2ccca3b
Changes based on PR comments
amsanghi Jul 19, 2024
854d74e
Merge branch 'master' into fast_confirmation
amsanghi Jul 19, 2024
478a219
fix test
amsanghi Jul 19, 2024
a13ddb6
skip race
amsanghi Jul 19, 2024
84c2fec
fix
amsanghi Jul 19, 2024
9985c67
Changes based on PR comments
amsanghi Jul 20, 2024
58caac6
Merge branch 'master' into fast_confirmation
PlasmaPower Jul 23, 2024
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ go-ethereum/tests
**/*.yml
contracts/build
contracts/cache/
safe-smart-account/build/
solgen/go
**/node_modules

Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@
[submodule "arbitrator/langs/bf"]
path = arbitrator/langs/bf
url = https://github.com/OffchainLabs/stylus-sdk-bf.git
[submodule "safe-smart-account"]
path = safe-smart-account
url = https://github.com/safe-global/safe-smart-account.git
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ WORKDIR /workspace
COPY contracts/package.json contracts/yarn.lock contracts/
RUN cd contracts && yarn install
COPY contracts contracts/
COPY safe-smart-account safe-smart-account/
RUN cd safe-smart-account && yarn install
COPY Makefile .
RUN . ~/.bashrc && NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-solidity

Expand Down Expand Up @@ -82,6 +84,7 @@ COPY ./wavmio ./wavmio
COPY ./zeroheavy ./zeroheavy
COPY ./contracts/src/precompiles/ ./contracts/src/precompiles/
COPY ./contracts/package.json ./contracts/yarn.lock ./contracts/
COPY ./safe-smart-account ./safe-smart-account
COPY ./solgen/gen.go ./solgen/
COPY ./fastcache ./fastcache
COPY ./go-ethereum ./go-ethereum
Expand Down Expand Up @@ -179,6 +182,7 @@ COPY ./Makefile ./
COPY ./arbitrator ./arbitrator
COPY ./solgen ./solgen
COPY ./contracts ./contracts
COPY ./safe-smart-account ./safe-smart-account
RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-replay-env

FROM debian:bookworm-slim AS machine-versions
Expand Down Expand Up @@ -226,6 +230,7 @@ COPY . ./
COPY --from=contracts-builder workspace/contracts/build/ contracts/build/
COPY --from=contracts-builder workspace/contracts/out/ contracts/out/
COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/UpgradeExecutor.json contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/
COPY --from=contracts-builder workspace/safe-smart-account/build/ safe-smart-account/build/
COPY --from=contracts-builder workspace/.make/ .make/
COPY --from=prover-header-export / target/
COPY --from=brotli-library-export / target/
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,14 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin)
go run solgen/gen.go
@touch $@

.make/solidity: $(DEP_PREDICATE) contracts/src/*/*.sol .make/yarndeps $(ORDER_ONLY_PREDICATE) .make
.make/solidity: $(DEP_PREDICATE) safe-smart-account/contracts/*/*.sol safe-smart-account/contracts/*.sol contracts/src/*/*.sol .make/yarndeps $(ORDER_ONLY_PREDICATE) .make
yarn --cwd safe-smart-account build
yarn --cwd contracts build
yarn --cwd contracts build:forge:yul
@touch $@

.make/yarndeps: $(DEP_PREDICATE) contracts/package.json contracts/yarn.lock $(ORDER_ONLY_PREDICATE) .make
yarn --cwd safe-smart-account install
yarn --cwd contracts install
@touch $@

Expand Down
1 change: 1 addition & 0 deletions safe-smart-account
Submodule safe-smart-account added at 192c7d
12 changes: 12 additions & 0 deletions solgen/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ func main() {
log.Fatal(err)
}

filePathsSafeSmartAccount, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*", "*.sol", "*.json"))
if err != nil {
log.Fatal(err)
}
filePathsSafeSmartAccountOuter, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*.sol", "*.json"))
if err != nil {
log.Fatal(err)
}

filePaths = append(filePaths, filePathsSafeSmartAccount...)
filePaths = append(filePaths, filePathsSafeSmartAccountOuter...)

modules := make(map[string]*moduleInfo)

for _, path := range filePaths {
Expand Down
239 changes: 239 additions & 0 deletions staker/fast_confirm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
// Copyright 2023-2024, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE

package staker

import (
"context"
"errors"
"fmt"
"math/big"
"sort"

"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/log"

"github.com/offchainlabs/nitro/solgen/go/contractsgen"
"github.com/offchainlabs/nitro/solgen/go/rollupgen"
"github.com/offchainlabs/nitro/staker/txbuilder"
"github.com/offchainlabs/nitro/util/headerreader"
)

type FastConfirmSafe struct {
safe *contractsgen.Safe
owners []common.Address
threshold uint64
fastConfirmNextNodeMethod abi.Method
builder *txbuilder.Builder
wallet ValidatorWalletInterface
gasRefunder common.Address
l1Reader *headerreader.HeaderReader
}

func NewFastConfirmSafe(
callOpts bind.CallOpts,
fastConfirmSafeAddress common.Address,
builder *txbuilder.Builder,
wallet ValidatorWalletInterface,
gasRefunder common.Address,
l1Reader *headerreader.HeaderReader,
) (*FastConfirmSafe, error) {
fastConfirmSafe := &FastConfirmSafe{
builder: builder,
wallet: wallet,
gasRefunder: gasRefunder,
l1Reader: l1Reader,
}
safe, err := contractsgen.NewSafe(fastConfirmSafeAddress, builder)
if err != nil {
return nil, err
}
fastConfirmSafe.safe = safe
owners, err := safe.GetOwners(&callOpts)
if err != nil {
return nil, err
}

// This is needed because safe contract needs owners to be sorted.
sort.Slice(owners, func(i, j int) bool {
return owners[i].Cmp(owners[j]) < 0
})
fastConfirmSafe.owners = owners
threshold, err := safe.GetThreshold(&callOpts)
if err != nil {
return nil, err
}
fastConfirmSafe.threshold = threshold.Uint64()
rollupUserLogicAbi, err := rollupgen.RollupUserLogicMetaData.GetAbi()
if err != nil {
return nil, err
}
fastConfirmNextNodeMethod, ok := rollupUserLogicAbi.Methods["fastConfirmNextNode"]
if !ok {
return nil, errors.New("RollupUserLogic ABI missing fastConfirmNextNode method")
}
fastConfirmSafe.fastConfirmNextNodeMethod = fastConfirmNextNodeMethod
return fastConfirmSafe, nil
}

func (f *FastConfirmSafe) tryFastConfirmation(ctx context.Context, blockHash common.Hash, sendRoot common.Hash) error {
fastConfirmCallData, err := f.createFastConfirmCalldata(blockHash, sendRoot)
if err != nil {
return err
}
callOpts := &bind.CallOpts{Context: ctx}
// Current nonce of the safe.
nonce, err := f.safe.Nonce(callOpts)
if err != nil {
return err
}
// Hash of the safe transaction.
safeTxHash, err := f.safe.GetTransactionHash(
callOpts,
f.wallet.RollupAddress(),
big.NewInt(0),
fastConfirmCallData,
0,
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
common.Address{},
common.Address{},
nonce,
)
if err != nil {
return err
}
if !f.wallet.CanBatchTxs() {
err = f.flushTransactions(ctx)
if err != nil {
return err
}
}
auth, err := f.builder.Auth(ctx)
if err != nil {
return err
}
_, err = f.safe.ApproveHash(auth, safeTxHash)
if err != nil {
return err
}
if !f.wallet.CanBatchTxs() {
err = f.flushTransactions(ctx)
if err != nil {
return err
}
}
executedTx, err := f.checkApprovedHashAndExecTransaction(ctx, fastConfirmCallData, safeTxHash)
if err != nil {
return err
}
if executedTx {
return nil
}
// If the transaction was not executed, we need to flush the transactions (for approve hash) and try again.
// This is because the hash might have been approved by another wallet in the same block,
// which might have led to a race condition.
err = f.flushTransactions(ctx)
if err != nil {
return err
}
_, err = f.checkApprovedHashAndExecTransaction(ctx, fastConfirmCallData, safeTxHash)
return err
}

func (f *FastConfirmSafe) flushTransactions(ctx context.Context) error {
arbTx, err := f.wallet.ExecuteTransactions(ctx, f.builder, f.gasRefunder)
if err != nil {
return err
}
if arbTx != nil {
_, err = f.l1Reader.WaitForTxApproval(ctx, arbTx)
if err == nil {
log.Info("successfully executed staker transaction", "hash", arbTx.Hash())
} else {
return fmt.Errorf("error waiting for tx receipt: %w", err)
}
}
f.builder.ClearTransactions()
return nil
}

func (f *FastConfirmSafe) createFastConfirmCalldata(
blockHash common.Hash, sendRoot common.Hash,
) ([]byte, error) {
calldata, err := f.fastConfirmNextNodeMethod.Inputs.Pack(
blockHash,
sendRoot,
)
if err != nil {
return nil, err
}
fullCalldata := append([]byte{}, f.fastConfirmNextNodeMethod.ID...)
fullCalldata = append(fullCalldata, calldata...)
return fullCalldata, nil
}

func (f *FastConfirmSafe) checkApprovedHashAndExecTransaction(ctx context.Context, fastConfirmCallData []byte, safeTxHash [32]byte) (bool, error) {
var signatures []byte
approvedHashCount := uint64(0)
for _, owner := range f.owners {
if f.wallet.Address() == nil {
return false, errors.New("wallet address is nil")
}
var approved *big.Int
// No need check if wallet has approved the hash,
// since checkApprovedHashAndExecTransaction is called only after wallet has approved the hash.
if *f.wallet.Address() == owner {
approved = common.Big1
} else {
var err error
approved, err = f.safe.ApprovedHashes(&bind.CallOpts{Context: ctx}, owner, safeTxHash)
if err != nil {
return false, err
}
}

// If the owner has approved the hash, we add the signature to the transaction.
// We add the signature in the format r, s, v.
// We set v to 1, as it is the only possible value for a approved hash.
// We set r to the owner's address.
// We set s to the empty hash.
// Refer to the Safe contract for more information.
if approved.Cmp(common.Big1) == 0 {
approvedHashCount++
v := uint8(1)
r := common.BytesToHash(owner.Bytes())
s := common.Hash{}
signatures = append(signatures, r.Bytes()...)
signatures = append(signatures, s.Bytes()...)
signatures = append(signatures, v)
}
}
if approvedHashCount >= f.threshold {
auth, err := f.builder.Auth(ctx)
if err != nil {
return false, err
}
_, err = f.safe.ExecTransaction(
amsanghi marked this conversation as resolved.
Show resolved Hide resolved
auth,
f.wallet.RollupAddress(),
big.NewInt(0),
fastConfirmCallData,
0,
big.NewInt(0),
big.NewInt(0),
big.NewInt(0),
common.Address{},
common.Address{},
signatures,
)
if err != nil {
return false, err
}
return true, nil
}
return false, nil
}
Loading
Loading