Commit 613e5dc4 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

op-chain-ops: delete dead code (#10266)

* op-chain-ops: delete dead code

This commit includes the deletion of a bunch of dead code
after https://github.com/ethereum-optimism/optimism/pull/10106
has been merged. There is no need to maintain a bunch of custom
go code to generate the L2 genesis anymore, all of that is handled
directly in solidity.

* op-chain-ops: delete more dead code

* op-chain-ops: more cleanup

* cleanup: remove old references

* op-chain-ops: cleanup abstractions
Co-authored-by: default avatarrefcell <abigger87@gmail.com>

---------
Co-authored-by: default avatarrefcell <abigger87@gmail.com>
parent 3be2820e
package deployer
import (
"context"
"errors"
"fmt"
"math/big"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
)
// TestKey is the same test key that geth uses
var TestKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// ChainID is the chain id used for simulated backends
var ChainID = big.NewInt(1337)
var TestAddress = crypto.PubkeyToAddress(TestKey.PublicKey)
var thousandETH = new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(1000))
type Constructor struct {
Name string
Args []interface{}
}
type SuperchainPredeploy struct {
Name string
CodeHash common.Hash
}
type Deployment struct {
Name string
Bytecode hexutil.Bytes
Address common.Address
}
type Deployer func(*backends.SimulatedBackend, *bind.TransactOpts, Constructor) (*types.Transaction, error)
// NewBackend returns a SimulatedBackend suitable for EVM simulation, without L2 features.
// It has up to Shanghai enabled.
// The returned backend should be closed after use.
func NewBackend() (*backends.SimulatedBackend, error) {
backend, err := NewBackendWithGenesisTimestamp(ChainID, 0, nil)
return backend, err
}
// NewBackendWithChainIDAndPredeploys returns a SimulatedBackend suitable for EVM simulation, without L2 features.
// It has up to Shanghai enabled, and allows for the configuration of the network's chain ID and predeploys.
// The returned backend should be closed after use.
func NewBackendWithChainIDAndPredeploys(chainID *big.Int, predeploys map[string]*common.Address) (*backends.SimulatedBackend, error) {
backend, err := NewBackendWithGenesisTimestamp(chainID, 0, predeploys)
return backend, err
}
func NewBackendWithGenesisTimestamp(chainID *big.Int, ts uint64, predeploys map[string]*common.Address) (*backends.SimulatedBackend, error) {
chainConfig := params.ChainConfig{
ChainID: chainID,
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: false,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0),
// Activated proof of stake. We manually build/commit blocks in the simulator anyway,
// and the timestamp verification of PoS is not against the wallclock,
// preventing blocks from getting stuck temporarily in the future-blocks queue, decreasing setup time a lot.
MergeNetsplitBlock: big.NewInt(0),
TerminalTotalDifficulty: big.NewInt(0),
TerminalTotalDifficultyPassed: true,
ShanghaiTime: u64ptr(0),
}
alloc := core.GenesisAlloc{
crypto.PubkeyToAddress(TestKey.PublicKey): core.GenesisAccount{
Balance: thousandETH,
},
}
for name, address := range predeploys {
bytecode, err := bindings.GetDeployedBytecode(name)
if err != nil {
return nil, err
}
alloc[*address] = core.GenesisAccount{
Code: bytecode,
}
}
cfg := ethconfig.Defaults
cfg.Preimages = true
cfg.Genesis = &core.Genesis{
Config: &chainConfig,
Timestamp: ts,
Difficulty: big.NewInt(0),
Alloc: alloc,
GasLimit: 30_000_000,
}
return backends.NewSimulatedBackendFromConfig(cfg), nil
}
func Deploy(backend *backends.SimulatedBackend, constructors []Constructor, cb Deployer) ([]Deployment, error) {
results := make([]Deployment, len(constructors))
opts, err := bind.NewKeyedTransactorWithChainID(TestKey, ChainID)
if err != nil {
return nil, err
}
opts.GasLimit = 15_000_000
ctx := context.Background()
for i, deployment := range constructors {
tx, err := cb(backend, opts, deployment)
if err != nil {
return nil, err
}
r, err := WaitMined(ctx, backend, tx)
if err != nil {
return nil, fmt.Errorf("%s: %w", deployment.Name, err)
}
addr := r.ContractAddress
if addr == (common.Address{}) {
return nil, fmt.Errorf("no address for %s", deployment.Name)
}
code, err := backend.CodeAt(context.Background(), addr, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s", deployment.Name)
}
if err != nil {
return nil, fmt.Errorf("cannot fetch code for %s", deployment.Name)
}
results[i] = Deployment{
Name: deployment.Name,
Bytecode: code,
Address: addr,
}
}
return results, nil
}
// DeployWithDeterministicDeployer deploys a smart contract on a simulated Ethereum blockchain using a deterministic deployment proxy (Arachnid's).
//
// Parameters:
// - backend: A pointer to backends.SimulatedBackend, representing the simulated Ethereum blockchain.
// Expected to have Arachnid's proxy deployer predeploys at 0x4e59b44847b379578588920cA78FbF26c0B4956C, NewBackendWithChainIDAndPredeploys handles this for you.
// - contractName: A string representing the name of the contract to be deployed.
//
// Returns:
// - []byte: The deployed bytecode of the contract.
// - error: An error object indicating any issues encountered during the deployment process.
//
// The function logs a fatal error and exits if there are any issues with transaction mining, if the deployment fails,
// or if the deployed bytecode is not found at the computed address.
func DeployWithDeterministicDeployer(backend *backends.SimulatedBackend, contractName string) ([]byte, error) {
cid, err := backend.ChainID(context.Background())
if err != nil {
return nil, err
}
opts, err := bind.NewKeyedTransactorWithChainID(TestKey, cid)
if err != nil {
return nil, fmt.Errorf("NewKeyedTransactorWithChainID failed: %w", err)
}
deployerAddress, err := bindings.GetDeployerAddress(contractName)
if err != nil {
return nil, err
}
deploymentSalt, err := bindings.GetDeploymentSalt(contractName)
if err != nil {
return nil, err
}
initBytecode, err := bindings.GetInitBytecode(contractName)
if err != nil {
return nil, err
}
transactor, err := bindings.NewDeterministicDeploymentProxyTransactor(common.BytesToAddress(deployerAddress), backend)
if err != nil {
return nil, fmt.Errorf("failed to initialize deployment proxy transactor at %s: %w", deployerAddress, err)
}
backend.Commit() // make sure at least one block is written or the below Fallback call can fail
tx, err := transactor.Fallback(opts, append(deploymentSalt, initBytecode...))
if err != nil {
return nil, fmt.Errorf("Fallback failed: %w", err)
}
receipt, err := WaitMined(context.Background(), backend, tx)
if err != nil {
return nil, fmt.Errorf("failed to get transaction receipt: %w", err)
}
if receipt.Status == 0 {
return nil, errors.New("failed to deploy contract using proxy deployer")
}
address := create2Address(
deployerAddress,
deploymentSalt,
initBytecode,
)
code, _ := backend.CodeAt(context.Background(), address, nil)
if len(code) == 0 {
return nil, fmt.Errorf("no code found for %s at: %s", contractName, address)
}
return code, nil
}
func u64ptr(n uint64) *uint64 {
return &n
}
// create2Address computes the Ethereum address for a contract created using the CREATE2 opcode.
//
// The CREATE2 opcode allows for more deterministic address generation in Ethereum, as it computes the
// address based on the creator's address, a salt value, and the contract's initialization code.
//
// Parameters:
// - creatorAddress: A byte slice representing the address of the account creating the contract.
// - salt: A byte slice representing the salt used in the address generation process. This can be any 32-byte value.
// - initCode: A byte slice representing the contract's initialization bytecode.
//
// Returns:
// - common.Address: The Ethereum address calculated using the CREATE2 opcode logic.
func create2Address(creatorAddress, salt, initCode []byte) common.Address {
payload := append([]byte{0xff}, creatorAddress...)
payload = append(payload, salt...)
initCodeHash := crypto.Keccak256(initCode)
payload = append(payload, initCodeHash...)
return common.BytesToAddress(crypto.Keccak256(payload)[12:])
}
// WaitMined waits for tx to be mined on the blockchain with a simulated backend, calling Commit()
// on the backend before attemping to fetch the transaction receipt in a wait loop. It stops
// waiting when the context is canceled.
func WaitMined(ctx context.Context, b *backends.SimulatedBackend, tx *types.Transaction) (*types.Receipt, error) {
queryTicker := time.NewTicker(100 * time.Millisecond)
defer queryTicker.Stop()
for {
// Call commit with each try since earlier calls may have preceded the tx reaching the
// txpool.
b.Commit()
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
if err == nil {
return receipt, nil
}
// Wait for the next round.
log.Warn("waiting on receipt due to error", "err", err)
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-queryTicker.C:
}
}
}
package deployer
import (
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestCreate2Address(t *testing.T) {
tests := []struct {
name string
creatorAddress []byte
salt []byte
initCode []byte
expectedAddress common.Address
}{
{
name: "SafeL2",
creatorAddress: common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C").Bytes(),
salt: common.FromHex("0000000000000000000000000000000000000000000000000000000000000000"),
expectedAddress: common.HexToAddress("0x3E5c63644E683549055b9Be8653de26E0B4CD36E"),
},
{
name: "MultiSendCallOnly",
creatorAddress: common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C").Bytes(),
salt: common.FromHex("0000000000000000000000000000000000000000000000000000000000000000"),
expectedAddress: common.HexToAddress("0x40A2aCCbd92BCA938b02010E17A5b8929b49130D"),
},
{
name: "MultiSend",
creatorAddress: common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C").Bytes(),
salt: common.FromHex("0000000000000000000000000000000000000000000000000000000000000000"),
expectedAddress: common.HexToAddress("0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761"),
},
{
name: "Permit2",
creatorAddress: common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C").Bytes(),
salt: common.FromHex("0000000000000000000000000000000000000000d3af2663da51c10215000000"),
expectedAddress: common.HexToAddress("0x000000000022D473030F116dDEE9F6B43aC78BA3"),
},
{
name: "EntryPoint",
creatorAddress: common.HexToAddress("0x4e59b44847b379578588920cA78FbF26c0B4956C").Bytes(),
salt: common.FromHex("0000000000000000000000000000000000000000000000000000000000000000"),
expectedAddress: common.HexToAddress("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"),
},
}
for _, test := range tests {
var err error
test.initCode, err = getInitCode(test.name)
if err != nil {
t.Error(err)
}
t.Run(test.name, func(t *testing.T) {
if got := create2Address(test.creatorAddress, test.salt, test.initCode); got != test.expectedAddress {
t.Errorf("expected: %x, want: %x", got, test.expectedAddress)
}
})
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -14,7 +14,6 @@ import ( ...@@ -14,7 +14,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
gstate "github.com/ethereum/go-ethereum/core/state" gstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -22,9 +21,6 @@ import ( ...@@ -22,9 +21,6 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
) )
...@@ -860,170 +856,6 @@ func (d *ForgeAllocs) UnmarshalJSON(b []byte) error { ...@@ -860,170 +856,6 @@ func (d *ForgeAllocs) UnmarshalJSON(b []byte) error {
return nil return nil
} }
// NewL2ImmutableConfig will create an ImmutableConfig given an instance of a
// DeployConfig and a block.
func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (*immutables.PredeploysImmutableConfig, error) {
if config.L1StandardBridgeProxy == (common.Address{}) {
return nil, fmt.Errorf("L1StandardBridgeProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1CrossDomainMessengerProxy == (common.Address{}) {
return nil, fmt.Errorf("L1CrossDomainMessengerProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1ERC721BridgeProxy == (common.Address{}) {
return nil, fmt.Errorf("L1ERC721BridgeProxy cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.SequencerFeeVaultRecipient == (common.Address{}) {
return nil, fmt.Errorf("SequencerFeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.BaseFeeVaultRecipient == (common.Address{}) {
return nil, fmt.Errorf("BaseFeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
if config.L1FeeVaultRecipient == (common.Address{}) {
return nil, fmt.Errorf("L1FeeVaultRecipient cannot be address(0): %w", ErrInvalidImmutablesConfig)
}
cfg := immutables.PredeploysImmutableConfig{
L2ToL1MessagePasser: struct{}{},
DeployerWhitelist: struct{}{},
WETH9: struct{}{},
L2CrossDomainMessenger: struct{}{},
L2StandardBridge: struct{}{},
SequencerFeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: config.SequencerFeeVaultRecipient,
MinWithdrawalAmount: (*big.Int)(config.SequencerFeeVaultMinimumWithdrawalAmount),
WithdrawalNetwork: config.SequencerFeeVaultWithdrawalNetwork.ToUint8(),
},
L1BlockNumber: struct{}{},
GasPriceOracle: struct{}{},
L1Block: struct{}{},
GovernanceToken: struct{}{},
LegacyMessagePasser: struct{}{},
L2ERC721Bridge: struct{}{},
OptimismMintableERC721Factory: struct {
Bridge common.Address
RemoteChainId *big.Int
}{
Bridge: predeploys.L2ERC721BridgeAddr,
RemoteChainId: new(big.Int).SetUint64(config.L1ChainID),
},
OptimismMintableERC20Factory: struct{}{},
ProxyAdmin: struct{}{},
BaseFeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: config.BaseFeeVaultRecipient,
MinWithdrawalAmount: (*big.Int)(config.BaseFeeVaultMinimumWithdrawalAmount),
WithdrawalNetwork: config.BaseFeeVaultWithdrawalNetwork.ToUint8(),
},
L1FeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: config.L1FeeVaultRecipient,
MinWithdrawalAmount: (*big.Int)(config.L1FeeVaultMinimumWithdrawalAmount),
WithdrawalNetwork: config.L1FeeVaultWithdrawalNetwork.ToUint8(),
},
SchemaRegistry: struct{}{},
EAS: struct {
Name string
}{
Name: "EAS",
},
Create2Deployer: struct{}{},
}
if err := cfg.Check(); err != nil {
return nil, err
}
return &cfg, nil
}
// NewL2StorageConfig will create a StorageConfig given an instance of a DeployConfig and genesis L1 anchor block.
func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.StorageConfig, error) {
storage := make(state.StorageConfig)
if block.Number() == nil {
return storage, errors.New("block number not set")
}
if block.BaseFee() == nil {
return storage, errors.New("block base fee not set")
}
storage["L2ToL1MessagePasser"] = state.StorageValues{
"msgNonce": 0,
}
storage["L2CrossDomainMessenger"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
"xDomainMsgSender": "0x000000000000000000000000000000000000dEaD",
"msgNonce": 0,
"otherMessenger": config.L1CrossDomainMessengerProxy,
}
storage["L2StandardBridge"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
"otherBridge": config.L1StandardBridgeProxy,
"messenger": predeploys.L2CrossDomainMessengerAddr,
}
storage["L2ERC721Bridge"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
"otherBridge": config.L1ERC721BridgeProxy,
"messenger": predeploys.L2CrossDomainMessengerAddr,
}
storage["OptimismMintableERC20Factory"] = state.StorageValues{
"_initialized": 1,
"_initializing": false,
"bridge": predeploys.L2StandardBridgeAddr,
}
excessBlobGas := block.ExcessBlobGas()
if excessBlobGas == nil {
excessBlobGas = u64ptr(0)
}
storage["L1Block"] = state.StorageValues{
"number": block.Number(),
"timestamp": block.Time(),
"basefee": block.BaseFee(),
"hash": block.Hash(),
"sequenceNumber": 0,
"blobBaseFeeScalar": config.GasPriceOracleBlobBaseFeeScalar,
"baseFeeScalar": config.GasPriceOracleBaseFeeScalar,
"batcherHash": eth.AddressAsLeftPaddedHash(config.BatchSenderAddress),
"l1FeeOverhead": config.GasPriceOracleOverhead,
"l1FeeScalar": config.GasPriceOracleScalar,
"blobBaseFee": eip4844.CalcBlobFee(*excessBlobGas),
}
storage["LegacyERC20ETH"] = state.StorageValues{
"_name": "Ether",
"_symbol": "ETH",
}
storage["WETH9"] = state.StorageValues{
"name": "Wrapped Ether",
"symbol": "WETH",
"decimals": 18,
}
if config.EnableGovernance {
storage["GovernanceToken"] = state.StorageValues{
"_name": config.GovernanceTokenName,
"_symbol": config.GovernanceTokenSymbol,
"_owner": config.GovernanceTokenOwner,
}
}
storage["ProxyAdmin"] = state.StorageValues{
"_owner": config.ProxyAdminOwner,
}
return storage, nil
}
type MarshalableRPCBlockNumberOrHash rpc.BlockNumberOrHash type MarshalableRPCBlockNumberOrHash rpc.BlockNumberOrHash
func (m *MarshalableRPCBlockNumberOrHash) MarshalJSON() ([]byte, error) { func (m *MarshalableRPCBlockNumberOrHash) MarshalJSON() ([]byte, error) {
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
gstate "github.com/ethereum/go-ethereum/core/state" gstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
...@@ -16,6 +17,11 @@ import ( ...@@ -16,6 +17,11 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
) )
// PrecompileCount represents the number of precompile addresses
// starting from `address(0)` to PrecompileCount that are funded
// with a single wei in the genesis state.
const PrecompileCount = 256
var ( var (
// uint128Max is type(uint128).max and is set in the init function. // uint128Max is type(uint128).max and is set in the init function.
uint128Max = new(big.Int) uint128Max = new(big.Int)
...@@ -91,3 +97,28 @@ func BuildL1DeveloperGenesis(config *DeployConfig, dump *gstate.Dump, l1Deployme ...@@ -91,3 +97,28 @@ func BuildL1DeveloperGenesis(config *DeployConfig, dump *gstate.Dump, l1Deployme
return memDB.Genesis(), nil return memDB.Genesis(), nil
} }
// CreateAccountNotExists creates the account in the `vm.StateDB` if it doesn't exist.
func CreateAccountNotExists(db vm.StateDB, account common.Address) {
if !db.Exist(account) {
db.CreateAccount(account)
}
}
// FundDevAccounts will fund each of the development accounts.
func FundDevAccounts(db vm.StateDB) {
for _, account := range DevAccounts {
CreateAccountNotExists(db, account)
db.AddBalance(account, uint256.MustFromBig(devBalance))
}
}
// SetPrecompileBalances will set a single wei at each precompile address.
// This is an optimization to make calling them cheaper.
func SetPrecompileBalances(db vm.StateDB) {
for i := 0; i < PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)})
CreateAccountNotExists(db, addr)
db.AddBalance(addr, uint256.NewInt(1))
}
}
package genesis
import (
"github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
)
// PrecompileCount represents the number of precompile addresses
// starting from `address(0)` to PrecompileCount that are funded
// with a single wei in the genesis state.
const PrecompileCount = 256
// FundDevAccounts will fund each of the development accounts.
func FundDevAccounts(db vm.StateDB) {
for _, account := range DevAccounts {
if !db.Exist(account) {
db.CreateAccount(account)
}
db.AddBalance(account, uint256.MustFromBig(devBalance))
}
}
// SetPrecompileBalances will set a single wei at each precompile address.
// This is an optimization to make calling them cheaper.
func SetPrecompileBalances(db vm.StateDB) {
for i := 0; i < PrecompileCount; i++ {
addr := common.BytesToAddress([]byte{byte(i)})
db.CreateAccount(addr)
db.AddBalance(addr, uint256.NewInt(1))
}
}
package immutables
import (
"fmt"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
)
// PredeploysImmutableConfig represents the set of L2 predeploys. It includes all
// L2 predeploys - not just ones with immutable values. This is to be very explicit
// about the configuration of the predeploys. It is important that the inner struct
// fields are in the same order as the constructor arguments in the solidity code.
type PredeploysImmutableConfig struct {
L2ToL1MessagePasser struct{}
DeployerWhitelist struct{}
WETH9 struct{}
L2CrossDomainMessenger struct{}
L2StandardBridge struct{}
SequencerFeeVault struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}
OptimismMintableERC20Factory struct{}
L1BlockNumber struct{}
GasPriceOracle struct{}
L1Block struct{}
GovernanceToken struct{}
LegacyMessagePasser struct{}
L2ERC721Bridge struct{}
OptimismMintableERC721Factory struct {
Bridge common.Address
RemoteChainId *big.Int
}
ProxyAdmin struct{}
BaseFeeVault struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}
L1FeeVault struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}
SchemaRegistry struct{}
EAS struct {
Name string
}
Create2Deployer struct{}
MultiCall3 struct{}
Safe_v130 struct{}
SafeL2_v130 struct{}
MultiSendCallOnly_v130 struct{}
SafeSingletonFactory struct{}
DeterministicDeploymentProxy struct{}
MultiSend_v130 struct{}
Permit2 struct{}
SenderCreator struct{}
EntryPoint struct{}
}
// Check will ensure that the required fields are set on the config.
// An error returned by `GetImmutableReferences` means that the solc compiler
// output for the contract has no immutables in it.
func (c *PredeploysImmutableConfig) Check() error {
return c.ForEach(func(name string, values any) error {
val := reflect.ValueOf(values)
if val.NumField() == 0 {
return nil
}
has, err := bindings.HasImmutableReferences(name)
exists := err == nil && has
isZero := val.IsZero()
// There are immutables defined in the solc output and
// the config is not empty.
if exists && !isZero {
return nil
}
// There are no immutables defined in the solc output and
// the config is empty
if !exists && isZero {
return nil
}
return fmt.Errorf("invalid immutables config: field %s: %w", name, err)
})
}
// ForEach will iterate over each of the fields in the config and call the callback
// with the value of the field as well as the field's name.
func (c *PredeploysImmutableConfig) ForEach(cb func(string, any) error) error {
val := reflect.ValueOf(c).Elem()
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
internalVal := reflect.ValueOf(field.Interface())
if err := cb(typ.Field(i).Name, internalVal.Interface()); err != nil {
return err
}
}
return nil
}
// DeploymentResults represents the output of deploying each of the
// contracts so that the immutables can be set properly in the bytecode.
type DeploymentResults map[string]hexutil.Bytes
// Deploy will deploy L2 predeploys that include immutables. This is to prevent the need
// for parsing the solc output to find the correct immutable offsets and splicing in the values.
// Skip any predeploys that do not have immutables as their bytecode will be directly inserted
// into the state. This does not currently support recursive structs.
func Deploy(config *PredeploysImmutableConfig) (DeploymentResults, error) {
if err := config.Check(); err != nil {
return DeploymentResults{}, err
}
deployments := make([]deployer.Constructor, 0)
val := reflect.ValueOf(config).Elem()
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
if reflect.ValueOf(field.Interface()).IsZero() {
continue
}
deployment := deployer.Constructor{
Name: typ.Field(i).Name,
Args: []any{},
}
internalVal := reflect.ValueOf(field.Interface())
for j := 0; j < internalVal.NumField(); j++ {
internalField := internalVal.Field(j)
deployment.Args = append(deployment.Args, internalField.Interface())
}
deployments = append(deployments, deployment)
}
results, err := deployContractsWithImmutables(deployments)
if err != nil {
return nil, fmt.Errorf("cannot deploy contracts with immutables: %w", err)
}
return results, nil
}
// deployContractsWithImmutables will deploy contracts to a simulated backend so that their immutables
// can be properly set. The bytecode returned in the results is suitable to be
// inserted into the state via state surgery.
func deployContractsWithImmutables(constructors []deployer.Constructor) (DeploymentResults, error) {
backend, err := deployer.NewBackend()
if err != nil {
return nil, err
}
defer backend.Close()
deployments, err := deployer.Deploy(backend, constructors, l2ImmutableDeployer)
if err != nil {
return nil, err
}
results := make(DeploymentResults)
for _, dep := range deployments {
results[dep.Name] = dep.Bytecode
}
return results, nil
}
// l2ImmutableDeployer will deploy L2 predeploys that contain immutables to the simulated backend.
// It only needs to care about the predeploys that have immutables so that the deployed bytecode
// has the dynamic value set at the correct location in the bytecode.
func l2ImmutableDeployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (*types.Transaction, error) {
var tx *types.Transaction
var recipient common.Address
var minimumWithdrawalAmount *big.Int
var withdrawalNetwork uint8
var err error
if has, err := bindings.HasImmutableReferences(deployment.Name); err != nil || !has {
return nil, fmt.Errorf("%s does not have immutables: %w", deployment.Name, err)
}
switch deployment.Name {
case "SequencerFeeVault":
recipient, minimumWithdrawalAmount, withdrawalNetwork, err = prepareFeeVaultArguments(deployment)
if err != nil {
return nil, err
}
_, tx, _, err = bindings.DeploySequencerFeeVault(opts, backend, recipient, minimumWithdrawalAmount, withdrawalNetwork)
case "BaseFeeVault":
recipient, minimumWithdrawalAmount, withdrawalNetwork, err = prepareFeeVaultArguments(deployment)
if err != nil {
return nil, err
}
_, tx, _, err = bindings.DeployBaseFeeVault(opts, backend, recipient, minimumWithdrawalAmount, withdrawalNetwork)
case "L1FeeVault":
recipient, minimumWithdrawalAmount, withdrawalNetwork, err = prepareFeeVaultArguments(deployment)
if err != nil {
return nil, err
}
_, tx, _, err = bindings.DeployL1FeeVault(opts, backend, recipient, minimumWithdrawalAmount, withdrawalNetwork)
case "OptimismMintableERC721Factory":
bridge, ok := deployment.Args[0].(common.Address)
if !ok {
return nil, fmt.Errorf("invalid type for bridge")
}
remoteChainId, ok := deployment.Args[1].(*big.Int)
if !ok {
return nil, fmt.Errorf("invalid type for remoteChainId")
}
_, tx, _, err = bindings.DeployOptimismMintableERC721Factory(opts, backend, bridge, remoteChainId)
case "EAS":
_, tx, _, err = bindings.DeployEAS(opts, backend)
default:
return tx, fmt.Errorf("unknown contract: %s", deployment.Name)
}
return tx, err
}
// prepareFeeVaultArguments is a helper function that parses the arguments for the fee vault contracts.
func prepareFeeVaultArguments(deployment deployer.Constructor) (common.Address, *big.Int, uint8, error) {
recipient, ok := deployment.Args[0].(common.Address)
if !ok {
return common.Address{}, nil, 0, fmt.Errorf("invalid type for recipient")
}
minimumWithdrawalAmountHex, ok := deployment.Args[1].(*big.Int)
if !ok {
return common.Address{}, nil, 0, fmt.Errorf("invalid type for minimumWithdrawalAmount")
}
withdrawalNetwork, ok := deployment.Args[2].(uint8)
if !ok {
return common.Address{}, nil, 0, fmt.Errorf("invalid type for withdrawalNetwork")
}
return recipient, minimumWithdrawalAmountHex, withdrawalNetwork, nil
}
package immutables_test
import (
"math/big"
"reflect"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/immutables"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
)
func TestBuildOptimism(t *testing.T) {
cfg := immutables.PredeploysImmutableConfig{
L2ToL1MessagePasser: struct{}{},
DeployerWhitelist: struct{}{},
WETH9: struct{}{},
L2CrossDomainMessenger: struct{}{},
L2StandardBridge: struct{}{},
SequencerFeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: common.HexToAddress("0x1234567890123456789012345678901234567890"),
MinWithdrawalAmount: big.NewInt(100),
WithdrawalNetwork: 0,
},
L1BlockNumber: struct{}{},
GasPriceOracle: struct{}{},
L1Block: struct{}{},
GovernanceToken: struct{}{},
LegacyMessagePasser: struct{}{},
L2ERC721Bridge: struct{}{},
OptimismMintableERC721Factory: struct {
Bridge common.Address
RemoteChainId *big.Int
}{
Bridge: predeploys.L2StandardBridgeAddr,
RemoteChainId: big.NewInt(1),
},
OptimismMintableERC20Factory: struct{}{},
ProxyAdmin: struct{}{},
BaseFeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: common.HexToAddress("0x1234567890123456789012345678901234567890"),
MinWithdrawalAmount: big.NewInt(200),
WithdrawalNetwork: 0,
},
L1FeeVault: struct {
Recipient common.Address
MinWithdrawalAmount *big.Int
WithdrawalNetwork uint8
}{
Recipient: common.HexToAddress("0x1234567890123456789012345678901234567890"),
MinWithdrawalAmount: big.NewInt(200),
WithdrawalNetwork: 1,
},
SchemaRegistry: struct{}{},
EAS: struct{ Name string }{
Name: "EAS",
},
Create2Deployer: struct{}{},
MultiCall3: struct{}{},
Safe_v130: struct{}{},
SafeL2_v130: struct{}{},
MultiSendCallOnly_v130: struct{}{},
SafeSingletonFactory: struct{}{},
DeterministicDeploymentProxy: struct{}{},
MultiSend_v130: struct{}{},
Permit2: struct{}{},
SenderCreator: struct{}{},
EntryPoint: struct{}{},
}
require.NoError(t, cfg.Check())
results, err := immutables.Deploy(&cfg)
require.NoError(t, err)
require.NotNil(t, results)
// Build a mapping of all of the predeploys
all := map[string]bool{}
// Build a mapping of the predeploys with immutable config
withConfig := map[string]bool{}
require.NoError(t, cfg.ForEach(func(name string, predeployConfig any) error {
all[name] = true
// If a predeploy has no config, it needs to have no immutable references in the solc output.
if reflect.ValueOf(predeployConfig).IsZero() {
ref, _ := bindings.HasImmutableReferences(name)
require.Zero(t, ref, "found immutable reference for %s", name)
return nil
}
withConfig[name] = true
return nil
}))
// Ensure that the PredeploysImmutableConfig is kept up to date
for name := range predeploys.Predeploys {
require.Truef(t, all[name], "predeploy %s not in set of predeploys", name)
ref, err := bindings.HasImmutableReferences(name)
// If there is predeploy config, there should be an immutable reference
if withConfig[name] {
require.NoErrorf(t, err, "error getting immutable reference for %s", name)
require.NotZerof(t, ref, "no immutable reference for %s", name)
} else {
require.Zero(t, ref, "found immutable reference for %s", name)
}
}
// Only the exact contracts that we care about are being modified
require.Equal(t, len(results), len(withConfig))
for name, bytecode := range results {
// There is bytecode there
require.Greater(t, len(bytecode), 0)
// It is in the set of contracts that we care about
require.Truef(t, withConfig[name], "contract %s not in set of contracts", name)
// The immutable reference is present
ref, err := bindings.HasImmutableReferences(name)
require.NoErrorf(t, err, "cannot get immutable reference for %s", name)
require.NotZerof(t, ref, "contract %s has no immutable reference", name)
}
}
package squash
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/state"
)
type staticChain struct {
startTime uint64
blockTime uint64
}
func (d *staticChain) Engine() consensus.Engine {
return ethash.NewFullFaker()
}
func (d *staticChain) GetHeader(h common.Hash, n uint64) *types.Header {
parentHash := common.Hash{0: 0xff}
binary.BigEndian.PutUint64(parentHash[1:], n-1)
return &types.Header{
ParentHash: parentHash,
UncleHash: types.EmptyUncleHash,
Coinbase: common.Address{},
Root: common.Hash{},
TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash,
Bloom: types.Bloom{},
Difficulty: big.NewInt(0),
Number: new(big.Int).SetUint64(n),
GasLimit: 30_000_000,
GasUsed: 0,
Time: d.startTime + n*d.blockTime,
Extra: nil,
MixDigest: common.Hash{},
Nonce: types.BlockNonce{},
BaseFee: big.NewInt(7),
WithdrawalsHash: &types.EmptyWithdrawalsHash,
}
}
type simState struct {
*state.MemoryStateDB
snapshotIndex int
tempAccessList map[common.Address]map[common.Hash]struct{}
}
var _ vm.StateDB = (*simState)(nil)
func (db *simState) AddressInAccessList(addr common.Address) bool {
_, ok := db.tempAccessList[addr]
return ok
}
func (db *simState) AddLog(log *types.Log) {
// no-op
}
func (db *simState) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
// return the latest state, instead of the pre-tx state.
acc, ok := db.Genesis().Alloc[addr]
if !ok {
return common.Hash{}
}
return acc.Storage[hash]
}
func (db *simState) AddSlotToAccessList(addr common.Address, slot common.Hash) {
// things like the fee-vault-address get marked as warm
m, ok := db.tempAccessList[addr]
if !ok {
m = make(map[common.Hash]struct{})
db.tempAccessList[addr] = m
}
m[slot] = struct{}{}
}
func (db *simState) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
m, addressOk := db.tempAccessList[addr]
if !addressOk {
return false, false
}
_, slotOk = m[slot]
return true, slotOk
}
func (db *simState) GetRefund() uint64 {
return 0
}
func (db *simState) AddAddressToAccessList(addr common.Address) {
if _, ok := db.tempAccessList[addr]; !ok {
db.tempAccessList[addr] = make(map[common.Hash]struct{})
}
}
func (db *simState) RevertToSnapshot(int) {
panic("RevertToSnapshot not supported")
}
func (db *simState) Snapshot() int {
db.snapshotIndex += 1
return db.snapshotIndex
}
// SquashSim wraps an op-chain-ops MemporyStateDB,
// and applies EVM-messages as if they all exist in the same infinite EVM block.
// The result is squashing all the EVM execution changes into the state.
type SquashSim struct {
chainConfig *params.ChainConfig
state *simState
evm *vm.EVM
signer types.Signer
}
// AddMessage processes a message on top of the chain-state that is squashed into a genesis state allocation.
func (sim *SquashSim) AddMessage(msg *core.Message) (res *core.ExecutionResult, err error) {
defer func() {
if rErr := recover(); rErr != nil {
err = fmt.Errorf("critical error: %v", rErr)
}
}()
// reset access-list
sim.state.tempAccessList = make(map[common.Address]map[common.Hash]struct{})
gp := new(core.GasPool)
gp.AddGas(30_000_000)
rules := sim.evm.ChainConfig().Rules(sim.evm.Context.BlockNumber, true, sim.evm.Context.Time)
sim.evm.StateDB.Prepare(rules, msg.From, predeploys.SequencerFeeVaultAddr, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
if !sim.state.Exist(msg.From) {
sim.state.CreateAccount(msg.From)
}
return core.ApplyMessage(sim.evm, msg, gp)
}
func (sim *SquashSim) BlockContext() *vm.BlockContext {
return &sim.evm.Context
}
// AddUpgradeTxs traverses a list of encoded deposit transactions.
// These transactions should match what would be included in the live system upgrade.
// The resulting state changes are squashed together, such that the final state can then be used as genesis state.
func (sim *SquashSim) AddUpgradeTxs(txs []hexutil.Bytes) error {
for i, otx := range txs {
var tx types.Transaction
if err := tx.UnmarshalBinary(otx); err != nil {
return fmt.Errorf("failed to decode upgrade tx %d: %w", i, err)
}
msg, err := core.TransactionToMessage(&tx, sim.signer, sim.BlockContext().BaseFee)
if err != nil {
return fmt.Errorf("failed to turn upgrade tx %d into message: %w", i, err)
}
if !msg.IsDepositTx {
return fmt.Errorf("upgrade tx %d is not a depost", i)
}
if res, err := sim.AddMessage(msg); err != nil {
return fmt.Errorf("invalid upgrade tx %d, EVM invocation failed: %w", i, err)
} else {
if res.Err != nil {
return fmt.Errorf("failed to successfully execute upgrade tx %d: %w", i, err)
}
}
}
return nil
}
func NewSimulator(db *state.MemoryStateDB) *SquashSim {
offsetBlocks := uint64(0)
genesisTime := uint64(17_000_000)
blockTime := uint64(2)
bc := &staticChain{startTime: genesisTime, blockTime: blockTime}
header := bc.GetHeader(common.Hash{}, genesisTime+offsetBlocks)
chainCfg := db.Genesis().Config
blockContext := core.NewEVMBlockContext(header, bc, nil, chainCfg, db)
vmCfg := vm.Config{}
signer := types.LatestSigner(db.Genesis().Config)
simDB := &simState{MemoryStateDB: db}
env := vm.NewEVM(blockContext, vm.TxContext{}, simDB, chainCfg, vmCfg)
return &SquashSim{
chainConfig: chainCfg,
state: simDB,
evm: env,
signer: signer,
}
}
...@@ -9,7 +9,6 @@ import ( ...@@ -9,7 +9,6 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/outputs"
faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types" faultTypes "github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
...@@ -26,11 +25,18 @@ import ( ...@@ -26,11 +25,18 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var (
// TestKey is the same test key that geth uses
TestKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
TestAddress = crypto.PubkeyToAddress(TestKey.PublicKey)
)
const ( const (
cannonGameType uint32 = 0 cannonGameType uint32 = 0
alphabetGameType uint32 = 255 alphabetGameType uint32 = 255
...@@ -112,7 +118,7 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) * ...@@ -112,7 +118,7 @@ func NewFactoryHelper(t *testing.T, ctx context.Context, system DisputeSystem) *
client := system.NodeClient("l1") client := system.NodeClient("l1")
chainID, err := client.ChainID(ctx) chainID, err := client.ChainID(ctx)
require.NoError(err) require.NoError(err)
opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, chainID) opts, err := bind.NewKeyedTransactorWithChainID(TestKey, chainID)
require.NoError(err) require.NoError(err)
l1Deployments := system.L1Deployments() l1Deployments := system.L1Deployments()
......
...@@ -10,7 +10,6 @@ import ( ...@@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts"
contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics" contractMetrics "github.com/ethereum-optimism/optimism/op-challenger/game/fault/contracts/metrics"
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace"
...@@ -225,7 +224,7 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC ...@@ -225,7 +224,7 @@ func (g *OutputCannonGameHelper) VerifyPreimage(ctx context.Context, outputRootC
execDepth := g.ExecDepth(ctx) execDepth := g.ExecDepth(ctx)
// Identifying the first state transition that loads a global preimage // Identifying the first state transition that loads a global preimage
provider, localContext := g.createCannonTraceProvider(ctx, "sequencer", outputRootClaim, challenger.WithPrivKey(deployer.TestKey)) provider, localContext := g.createCannonTraceProvider(ctx, "sequencer", outputRootClaim, challenger.WithPrivKey(TestKey))
start := uint64(0) start := uint64(0)
found := false found := false
for offset := uint32(0); ; offset += 4 { for offset := uint32(0); ; offset += 4 {
......
...@@ -6,7 +6,6 @@ import ( ...@@ -6,7 +6,6 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame"
...@@ -119,7 +118,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { ...@@ -119,7 +118,7 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
require.Truef(t, game.AvailableCredit(ctx, alice).Cmp(big.NewInt(0)) > 0, "Expected alice credit to be above zero") require.Truef(t, game.AvailableCredit(ctx, alice).Cmp(big.NewInt(0)) > 0, "Expected alice credit to be above zero")
// The actor should have no credit available because all its bonds were paid to Alice. // The actor should have no credit available because all its bonds were paid to Alice.
actorCredit := game.AvailableCredit(ctx, deployer.TestAddress) actorCredit := game.AvailableCredit(ctx, disputegame.TestAddress)
require.True(t, actorCredit.Cmp(big.NewInt(0)) == 0, "Expected alice available credit to be zero") require.True(t, actorCredit.Cmp(big.NewInt(0)) == 0, "Expected alice available credit to be zero")
// Advance the time past the weth unlock delay // Advance the time past the weth unlock delay
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment