Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 27 additions & 0 deletions espresso/devnet-tests/devnet_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package devnet_tests
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
"io"
Expand All @@ -21,6 +22,7 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup"
opclient "github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -644,3 +646,28 @@ func (d *Devnet) rollupClient(service string, port uint16) (*sources.RollupClien
client := sources.NewRollupClient(rpc)
return client, nil
}

// OperatorAddress returns the operator address (index 0) from the mnemonic.
// This is the address that runs deployment transactions and owns deployed contracts.
func (d *Devnet) OperatorAddress() (common.Address, error) {
// The operator is at index 0 of the mnemonic: "m/44'/60'/0'/0/0"
operatorPath := "m/44'/60'/0'/0/0"
account := accounts.Account{URL: accounts.URL{Path: operatorPath}}

operatorKey, err := d.secrets.Wallet.PrivateKey(account)
if err != nil {
return common.Address{}, err
}

return crypto.PubkeyToAddress(operatorKey.PublicKey), nil
}

// OperatorPrivateKey returns the operator private key (index 0) from the mnemonic.
// This is the private key that runs deployment transactions and owns deployed contracts.
func (d *Devnet) OperatorPrivateKey() (*ecdsa.PrivateKey, error) {
// The operator is at index 0 of the mnemonic: "m/44'/60'/0'/0/0"
operatorPath := "m/44'/60'/0'/0/0"
account := accounts.Account{URL: accounts.URL{Path: operatorPath}}

return d.secrets.Wallet.PrivateKey(account)
}
36 changes: 31 additions & 5 deletions espresso/devnet-tests/key_rotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,44 @@ func TestChangeBatchInboxOwner(t *testing.T) {
config, err := d.RollupConfig(ctx)
require.NoError(t, err)

// Change the BatchAuthenticator's owner
batchAuthenticator, err := bindings.NewBatchAuthenticator(config.BatchAuthenticatorAddress, d.L1)
require.NoError(t, err)
tx, err := batchAuthenticator.TransferOwnership(&bind.TransactOpts{}, d.secrets.Addresses().Bob)
currentOwner, err := batchAuthenticator.Owner(&bind.CallOpts{})
require.NoError(t, err)
_, err = d.SendL1Tx(ctx, tx)

// The BatchAuthenticator should be owned by the deployer
deployerAddress := d.secrets.Addresses().Deployer
aliceAddress := d.secrets.Addresses().Alice

t.Logf("Current owner: %s", currentOwner.Hex())
t.Logf("Deployer address: %s", deployerAddress.Hex())

// Verify the contract is owned by the deployer (as expected from deployment)
require.Equal(t, currentOwner, deployerAddress,
"BatchAuthenticator should be owned by deployer %s, but is owned by %s",
deployerAddress.Hex(), currentOwner.Hex())

require.NotEqual(t, deployerAddress, aliceAddress, "Alice should not be the current owner")

// Create transaction options using the deployer's private key
chainID, err := d.L1.ChainID(ctx)
require.NoError(t, err)

ownerAuth, err := bind.NewKeyedTransactorWithChainID(d.secrets.Deployer, chainID)
require.NoError(t, err)

tx, err := batchAuthenticator.TransferOwnership(ownerAuth, aliceAddress)
require.NoError(t, err, "Ownership transfer transaction building failed.")

// Send transaction using the devnet's SendL1Tx method
receipt, err := d.SendL1Tx(ctx, tx)
require.NoError(t, err, "Failed to send ownership transfer transaction.")
require.Equal(t, receipt.Status, uint64(1), "Transaction failed")

// Ensure the owner has been changed
newOwner, err := batchAuthenticator.Owner(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, newOwner, d.secrets.Addresses().Bob)
require.NoError(t, err, "Failed to get new owner.")
require.Equal(t, newOwner, aliceAddress, "New Owner is not Alice")

// Check that everything still functions
require.NoError(t, d.RunSimpleL2Burn())
Expand Down
7 changes: 4 additions & 3 deletions op-deployer/pkg/deployer/opcm/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ type DeployAWSNitroVerifierOutput struct {
}

type DeployEspressoInput struct {
Salt common.Hash
PreApprovedBatcherKey common.Address
NitroTEEVerifier common.Address
Salt common.Hash
PreApprovedBatcherKey common.Address
NitroTEEVerifier common.Address
BatchAuthenticatorOwner common.Address
}

type DeployEspressoOutput struct {
Expand Down
9 changes: 6 additions & 3 deletions op-deployer/pkg/deployer/pipeline/espresso.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ func DeployEspresso(env *Env, intent *state.Intent, st *state.State, chainID com
return fmt.Errorf("failed to deploy nitro verifier contracts: %w", err)
}

lgr.Info("deploying espresso contracts with BatchAuthenticator owner", "owner", env.Deployer.Hex())

var eo opcm.DeployEspressoOutput
eo, err = opcm.DeployEspresso(env.L1ScriptHost, opcm.DeployEspressoInput{
Salt: st.Create2Salt,
PreApprovedBatcherKey: chainIntent.PreApprovedBatcherKey,
NitroTEEVerifier: nvo.NitroTEEVerifierAddress,
Salt: st.Create2Salt,
PreApprovedBatcherKey: chainIntent.PreApprovedBatcherKey,
NitroTEEVerifier: nvo.NitroTEEVerifierAddress,
BatchAuthenticatorOwner: env.Deployer,
})
if err != nil {
return fmt.Errorf("failed to deploy espresso contracts: %w", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ interface IBatchAuthenticator {

function validBatchInfo(bytes32) external view returns (bool);

function initialize(address _owner) external;

function __constructor__(
address _espressoTEEVerifier,
address _preApprovedBatcher
Expand Down
21 changes: 21 additions & 0 deletions packages/contracts-bedrock/scripts/deploy/DeployEspresso.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.22;
import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol";
import { IBatchInbox } from "interfaces/L1/IBatchInbox.sol";
import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { DeployUtils } from "scripts/libraries/DeployUtils.sol";
import { Solarray } from "scripts/libraries/Solarray.sol";
import { IBatchAuthenticator } from "interfaces/L1/IBatchAuthenticator.sol";
Expand All @@ -16,6 +17,7 @@ contract DeployEspressoInput is BaseDeployIO {
bytes32 internal _salt;
address internal _preApprovedBatcherKey;
address internal _nitroTEEVerifier;
address internal _batchAuthenticatorOwner;

function set(bytes4 _sel, bytes32 _val) public {
if (_sel == this.salt.selector) _salt = _val;
Expand All @@ -27,6 +29,8 @@ contract DeployEspressoInput is BaseDeployIO {
_preApprovedBatcherKey = _val;
} else if (_sel == this.nitroTEEVerifier.selector) {
_nitroTEEVerifier = _val;
} else if (_sel == this.batchAuthenticatorOwner.selector) {
_batchAuthenticatorOwner = _val;
} else {
revert("DeployEspressoInput: unknown selector");
}
Expand All @@ -44,6 +48,10 @@ contract DeployEspressoInput is BaseDeployIO {
function preApprovedBatcherKey() public view returns (address) {
return _preApprovedBatcherKey;
}

function batchAuthenticatorOwner() public view returns (address) {
return _batchAuthenticatorOwner;
}
}

contract DeployEspressoOutput is BaseDeployIO {
Expand Down Expand Up @@ -74,8 +82,21 @@ contract DeployEspressoOutput is BaseDeployIO {

contract DeployEspresso is Script {
function run(DeployEspressoInput input, DeployEspressoOutput output) public {
// For now, let's hardcode the deployer address to ensure it works
// Deployer address is at index 3 of the mnemonic: 0x90F79bf6EB2c4f870365E785982E1f101E93b906
address deployerAddress = 0x90F79bf6EB2c4f870365E785982E1f101E93b906;
console.log("Using hardcoded deployer address as BatchAuthenticator owner:", deployerAddress);
run(input, output, deployerAddress);
}

function run(DeployEspressoInput input, DeployEspressoOutput output, address batchAuthenticatorOwner) public {
IEspressoTEEVerifier teeVerifier = deployTEEVerifier(input);
IBatchAuthenticator batchAuthenticator = deployBatchAuthenticator(input, output, teeVerifier);

// Initialize the BatchAuthenticator with the specified owner
vm.broadcast(msg.sender);
batchAuthenticator.initialize(batchAuthenticatorOwner);

deployBatchInbox(input, output, batchAuthenticator);
checkOutput(output);
}
Expand Down
7 changes: 7 additions & 0 deletions packages/contracts-bedrock/src/L1/BatchAuthenticator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ contract BatchAuthenticator is ISemver, OwnableUpgradeable {
nitroValidator = INitroValidator(address(espressoTEEVerifier.espressoNitroTEEVerifier()));
}

/// @notice Initializes the contract with the initial owner
/// @param _owner The initial owner of the contract
function initialize(address _owner) external initializer {
__Ownable_init();
_transferOwnership(_owner);
}

function decodeAttestationTbs(bytes memory attestation) external view returns (bytes memory, bytes memory) {
return nitroValidator.decodeAttestationTbs(attestation);
}
Expand Down