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

Fix signer registration via attestation #33

Merged
merged 3 commits into from
Dec 11, 2024
Merged
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
46 changes: 32 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,39 @@ guard-%:

define abigen
echo "Generating bindings for $(1)"
jq -r '.bytecode.object' out/$(1).sol/$(1).json > out/$(1).sol/$(1).bin
cp out/$(1).sol/$(1).$(3).json out/$(1).sol/$(1).json 2>/dev/null || true
jq -r '.bytecode.object' out/$(1).sol/$(1).json > out/$(1).sol/$(1).bin
jq -r '.abi' out/$(1).sol/$(1).json > out/$(1).sol/$(1).abi
abigen --abi out/$(1).sol/$(1).abi --bin out/$(1).sol/$(1).bin --pkg bindings --type $(1) --out bindings/$(2).go
endef

define verify
deploy=$(1); \
version=$(2); \
addresses=$$(jq -r '.transactions[] | select(.transactionType=="CREATE" or .transactionType=="CREATE2") | .contractAddress' $$deploy); \
for address in $$addresses; do \
name=$$(jq -r --arg address "$$address" '.transactions[] | select((.transactionType=="CREATE" or .transactionType=="CREATE2") and .contractAddress==$$address) | .contractName' $$deploy); \
arguments=$$(jq -r --arg address "$$address" '.transactions[] | select((.transactionType=="CREATE" or .transactionType=="CREATE2") and .contractAddress==$$address) | .arguments // [] | join(" ")' $$deploy); \
namewithoutversion=$${name%.*.*.*}; \
constructor=$$(jq '.abi[] | select(.type=="constructor")' out/$$namewithoutversion.sol/$$name.json | jq -r '.inputs | map(.type) | join(",")'); \
echo; \
echo "Verifying $$namewithoutversion @ $$address using constructor($$constructor) $$arguments"; \
constructor_args=$$(cast abi-encode "constructor($$constructor)" $$arguments); \
forge verify-contract --compiler-version $$version --watch --verifier-url https://api-sepolia.basescan.org/api --constructor-args $$constructor_args $$address $$namewithoutversion ; \
done
endef

.PHONY: bindings
bindings:
go install github.com/ethereum/go-ethereum/cmd/[email protected]
forge build
mkdir -p bindings
@$(call abigen,"OutputOracle","output_oracle")
@$(call abigen,"Portal","portal")
@$(call abigen,"DeployChain","deploy_chain")
@$(call abigen,"OutputOracle","output_oracle","0.8.15")
@$(call abigen,"Portal","portal","0.8.15")
@$(call abigen,"DeployChain","deploy_chain","0.8.15")
@$(call abigen,"CertManager","cert_manager","0.8.15")
@$(call abigen,"SystemConfigGlobal","system_config_global","0.8.15")
@$(call abigen,"GnosisSafe","gnosis_safe","0.8.15")

.PHONY: deploy-cert-manager
deploy-cert-manager: guard-IMPL_SALT guard-DEPLOY_PRIVATE_KEY guard-RPC_URL
Expand All @@ -27,20 +47,18 @@ deploy: guard-IMPL_SALT guard-DEPLOY_CONFIG_PATH guard-DEPLOY_PRIVATE_KEY guard-
@forge script DeploySystem --sig deploy --rpc-url $(RPC_URL) \
--private-key $(DEPLOY_PRIVATE_KEY) --broadcast

.PHONY: deploy-deploy-chain
deploy-deploy-chain: guard-IMPL_SALT guard-DEPLOY_PRIVATE_KEY guard-RPC_URL
@forge script DeployDeployChain --rpc-url $(RPC_URL) \
--private-key $(DEPLOY_PRIVATE_KEY) --broadcast

.PHONY: testnet
testnet: guard-L1_URL guard-DEPLOY_PRIVATE_KEY
DEPLOY_CHAIN_ADDRESS=$${DEPLOY_CHAIN_ADDRESS:-$$(jq -r ".DeployChain" deployments/84532-deploy.json)} \
go run ./testnet

.PHONY: verify
verify:
deploy=broadcast/DeploySystem.s.sol/84532/run-1733867021.json; \
addresses=$$(jq -r '.transactions[] | select(.transactionType=="CREATE" or .transactionType=="CREATE2") | .contractAddress' $$deploy); \
for address in $$addresses; do \
name=$$(jq -r --arg address "$$address" '.transactions[] | select((.transactionType=="CREATE" or .transactionType=="CREATE2") and .contractAddress==$$address) | .contractName' $$deploy); \
arguments=$$(jq -r --arg address "$$address" '.transactions[] | select((.transactionType=="CREATE" or .transactionType=="CREATE2") and .contractAddress==$$address) | .arguments // [] | join(" ")' $$deploy); \
constructor=$$(jq '.abi[] | select(.type=="constructor")' out/$$name.sol/$$name.json | jq -r '.inputs | map(.type) | join(",")'); \
echo "Verifying $$name @ $$address using constructor($$constructor) $$arguments"; \
constructor_args=$$(cast abi-encode "constructor($$constructor)" $$arguments); \
forge verify-contract --watch --verifier-url https://api-sepolia.basescan.org/api --constructor-args $$constructor_args $$address $$name ; \
done
@$(call verify,"broadcast/DeployCertManager.s.sol/84532/run-1733890597.json","0.8.24")
@$(call verify,"broadcast/DeploySystem.s.sol/84532/run-1733867021.json","0.8.15")
@$(call verify,"broadcast/DeployDeployChain.s.sol/84532/run-1733884066.json","0.8.15")
595 changes: 595 additions & 0 deletions bindings/cert_manager.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bindings/deploy_chain.go

Large diffs are not rendered by default.

3,152 changes: 3,152 additions & 0 deletions bindings/gnosis_safe.go

Large diffs are not rendered by default.

1,295 changes: 1,295 additions & 0 deletions bindings/system_config_global.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion deployments/84532-certmanager.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"CertManager": "0x00a452e7B56052f0beC5EF863F77eDDfd81938C4"
"CertManager": "0xD42fd50A9A8eE3F127A11AEACD4ADAA67Da7FE3B"
}
4 changes: 2 additions & 2 deletions deployments/84532-deploy.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"AddressManager": "0xdcB1Ee2F0F35F8053C1EbD700c030180f7b2C14b",
"AnchorStateRegistryProxy": "0x0000000000000000000000000000000000000001",
"CertManager": "0x00a452e7B56052f0beC5EF863F77eDDfd81938C4",
"CertManager": "0xD42fd50A9A8eE3F127A11AEACD4ADAA67Da7FE3B",
"DelayedWETHProxy": "0x0000000000000000000000000000000000000001",
"DeployChain": "0x8B4dB9468126EA0AA6EC8f1FAEb32173de3A27c7",
"DisputeGameFactoryProxy": "0x0000000000000000000000000000000000000001",
Expand All @@ -27,7 +27,7 @@
"SuperchainConfig": "0xC77dB710C47b6e294D3d544572a10187e8Ef6b2C",
"SuperchainConfigProxy": "0xCf940f9c053092d07EB62DaB59D0AFddF426dE67",
"SystemConfig": "0x8aB8559E6C661eFEB0a44C0f08E180CEe344dABE",
"SystemConfigGlobal": "0x9C9a3B1c8676c1E0A6Ebb9402E7354930Bc52A59",
"SystemConfigGlobal": "0x0933e802B0977fe6287C48a0F601b39244118E04",
"SystemConfigGlobalProxy": "0x53200eC3d6E91E7Ba1fD1087D38430F43501C9Fb",
"SystemConfigProxy": "0x57708f73fF01e8697799B38f47Fbd65bDf9138Bc",
"SystemOwnerSafe": "0xFCD4AfF397A2F9D2a435B64AdA1A70efC59310aD"
Expand Down
2 changes: 1 addition & 1 deletion lib/nitro-validator
181 changes: 181 additions & 0 deletions register-signer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package main

import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"os"
"time"

"github.com/base-org/op-enclave/bindings"
"github.com/base-org/op-enclave/op-withdrawer/withdrawals"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/hf/nitrite"
)

type deployment struct {
SystemConfigGlobalProxy common.Address `json:"SystemConfigGlobalProxy"`
}

func main() {
var attestationHex string
var rpcUrl string
var privateKeyHex string
var deploymentFile string
flag.StringVar(&attestationHex, "attestation", "", "attestation hex")
flag.StringVar(&rpcUrl, "rpc", "https://sepolia.base.org", "rpc url")
flag.StringVar(&privateKeyHex, "private-key", "", "private key")
flag.StringVar(&deploymentFile, "deployment", "deployments/84532-deploy.json", "deployment file")
flag.Parse()

if attestationHex == "" || privateKeyHex == "" {
flag.Usage()
os.Exit(1)
}

attestation, err := hexutil.Decode(attestationHex)
if err != nil {
panic(err)
}
privateKey, err := hexutil.Decode(privateKeyHex)
if err != nil {
panic(err)
}

res, err := nitrite.Verify(attestation, nitrite.VerifyOptions{})
if err != nil {
panic(err)
}

ctx := context.Background()
client, err := ethclient.DialContext(ctx, rpcUrl)
if err != nil {
panic(err)
}

deploy, err := os.ReadFile(deploymentFile)
if err != nil {
panic(err)
}
var d deployment
err = json.Unmarshal(deploy, &d)
if err != nil {
panic(err)
}

if bytes.Equal(common.Address{}.Bytes(), d.SystemConfigGlobalProxy.Bytes()) {
panic("SystemConfigGlobalProxy address not found in deployment file")
}

key, err := crypto.ToECDSA(privateKey)
if err != nil {
panic(err)
}

chainId, err := client.ChainID(ctx)
if err != nil {
panic(err)
}
signer := types.LatestSignerForChainID(chainId)
auth := &bind.TransactOpts{
From: crypto.PubkeyToAddress(key.PublicKey),
Signer: func(_ common.Address, tx *types.Transaction) (*types.Transaction, error) {
return types.SignTx(tx, signer, key)
},
}

systemConfigGlobal, err := bindings.NewSystemConfigGlobal(d.SystemConfigGlobalProxy, client)
if err != nil {
panic(err)
}

pub, err := crypto.UnmarshalPubkey(res.Document.PublicKey)
if err != nil {
panic(err)
}
signerAddr := crypto.PubkeyToAddress(*pub)
validSigner, err := systemConfigGlobal.ValidSigners(&bind.CallOpts{}, signerAddr)
if err != nil {
panic(err)
}
fmt.Printf("Public key: %s\n", hexutil.Encode(res.Document.PublicKey))
fmt.Printf("Signer: %s\n", signerAddr.String())
if validSigner {
fmt.Printf("Signer already registered: %s\n", signerAddr.String())
return
}

certManagerAddr, err := systemConfigGlobal.CertManager(&bind.CallOpts{})
if err != nil {
panic(err)
}
certManager, err := bindings.NewCertManager(certManagerAddr, client)
if err != nil {
panic(err)
}

verifyCert := func(cert []byte, ca bool, parentCertHash common.Hash) common.Hash {
certHash := crypto.Keccak256Hash(cert)
verified, err := certManager.Verified(&bind.CallOpts{}, certHash)
if err != nil {
panic(err)
}
if len(verified) == 0 {
tx, err := certManager.VerifyCert(auth, cert, ca, parentCertHash)
if err != nil {
panic(err)
}
receipt, err := withdrawals.WaitForReceipt(ctx, client, tx.Hash(), 2*time.Second)
if err != nil {
panic(err)
}
fmt.Printf("Verified cert: %s, tx: %s\n", certHash.String(), receipt.TxHash.String())
} else {
fmt.Printf("Cert already verified: %s\n", certHash.String())
}
return certHash
}

parentCertHash := crypto.Keccak256Hash(res.Document.CABundle[0])
for i := 0; i < len(res.Document.CABundle); i++ {
cert := res.Document.CABundle[i]
parentCertHash = verifyCert(cert, true, parentCertHash)
}
verifyCert(res.Document.Certificate, false, parentCertHash)

pcr0Hash := crypto.Keccak256Hash(res.Document.PCRs[0])
valid, err := systemConfigGlobal.ValidPCR0s(&bind.CallOpts{}, pcr0Hash)
if err != nil {
panic(err)
}
if !valid {
tx, err := systemConfigGlobal.RegisterPCR0(auth, res.Document.PCRs[0])
if err != nil {
panic(err)
}
receipt, err := withdrawals.WaitForReceipt(ctx, client, tx.Hash(), 2*time.Second)
if err != nil {
panic(err)
}
fmt.Printf("Registered PCR0, tx: %s\n", receipt.TxHash.String())
} else {
fmt.Printf("PCR0 already registered: %s\n", pcr0Hash.String())
}

tx, err := systemConfigGlobal.RegisterSigner(auth, res.COSESign1, res.Signature)
if err != nil {
panic(err)
}
receipt, err := withdrawals.WaitForReceipt(ctx, client, tx.Hash(), 2*time.Second)
if err != nil {
panic(err)
}
fmt.Printf("Registered signer, tx: %s\n", receipt.TxHash.String())
}
47 changes: 47 additions & 0 deletions script/UpgradeSystemConfigGlobal.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import {Script} from "forge-std/Script.sol";
import {console2 as console} from "forge-std/console2.sol";
import {DeployChain} from "../src/DeployChain.sol";
import {Artifacts} from "@eth-optimism-bedrock/scripts/Artifacts.s.sol";
import {ProxyAdmin} from "@eth-optimism-bedrock/src/universal/ProxyAdmin.sol";
import {SystemConfigGlobal} from "../src/SystemConfigGlobal.sol";
import {ICertManager} from "@nitro-validator/ICertManager.sol";
import {IGnosisSafe, Enum} from "@eth-optimism-bedrock/scripts/interfaces/IGnosisSafe.sol";

contract UpgradeSystemConfigGlobal is Script, Artifacts {
function run() public {
_loadAddresses(deploymentOutfile);

bytes memory signature = abi.encodePacked(uint256(uint160(msg.sender)), bytes32(0), uint8(1));

console.log("Deploying SystemConfigGlobal implementation");
vm.startBroadcast();

address addr_ = address(new SystemConfigGlobal{salt: _implSalt()}(ICertManager(mustGetAddress("CertManager"))));
bytes memory data =
abi.encodeCall(ProxyAdmin.upgrade, (payable(mustGetAddress("SystemConfigGlobalProxy")), address(addr_)));
IGnosisSafe(mustGetAddress("SystemOwnerSafe")).execTransaction({
to: mustGetAddress("ProxyAdmin"),
value: 0,
data: data,
operation: Enum.Operation.Call,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: address(0),
refundReceiver: payable(address(0)),
signatures: signature
});

vm.stopBroadcast();

delete _namedDeployments["SystemConfigGlobal"];
save("SystemConfigGlobal", addr_);
}

function _implSalt() internal view returns (bytes32 _env) {
_env = keccak256(bytes(vm.envOr("IMPL_SALT", string("ethers phoenix"))));
}
}
9 changes: 6 additions & 3 deletions src/SystemConfigGlobal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ pragma solidity ^0.8.0;
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {ISemver} from "@eth-optimism-bedrock/src/universal/interfaces/ISemver.sol";
import {NitroValidator} from "@nitro-validator/NitroValidator.sol";
import {CborDecode} from "@nitro-validator/CborDecode.sol";
import {LibBytes} from "@nitro-validator/LibBytes.sol";
import {LibCborElement, CborElement, CborDecode} from "@nitro-validator/CborDecode.sol";
import {ICertManager} from "@nitro-validator/ICertManager.sol";

contract SystemConfigGlobal is OwnableUpgradeable, ISemver, NitroValidator {
using LibBytes for bytes;
using CborDecode for bytes;
using LibCborElement for CborElement;

uint256 public constant MAX_AGE = 60 minutes;

Expand Down Expand Up @@ -56,8 +59,8 @@ contract SystemConfigGlobal is OwnableUpgradeable, ISemver, NitroValidator {

require(ptrs.timestamp + MAX_AGE > block.timestamp, "attestation too old");

bytes memory publicKey = attestationTbs.slice(ptrs.publicKey);
address enclaveAddress = address(uint160(uint256(keccak256(publicKey))));
bytes32 publicKeyHash = attestationTbs.keccak(ptrs.publicKey.start() + 1, ptrs.publicKey.length() - 1);
address enclaveAddress = address(uint160(uint256(publicKeyHash)));
validSigners[enclaveAddress] = true;
}

Expand Down
2 changes: 1 addition & 1 deletion test/SystemConfigGlobal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ contract SystemConfigGlobalTest is Test {
(bytes memory attestationTbs, bytes memory signature) = systemConfigGlobal.decodeAttestationTbs(attestation);
systemConfigGlobal.registerSigner(attestationTbs, signature);

address expectedSigner = 0xe04d808785d2BBdE18E9D0C01c05FB8CE0711f2d;
address expectedSigner = 0x874a4c5675cd4850dB08bD9A1e3184ED239087e4;
assertTrue(systemConfigGlobal.validSigners(expectedSigner));
}
}
Loading