Commit 6fa5e378 authored by Yann Hodique's avatar Yann Hodique Committed by GitHub

feat(kurtosis-devnet): output L1 addresses (#13601)

Dump a few potentially interesting addresses as part of the L1 manifest.
In particular, superchain addresses.

Implementation addresses are provided as well, as they might come in
handy for debugging purposes.

Output now looks like:
...
  "l1": {
    "name": "Ethereum",
    "nodes": [
      {
        "cl": "http://127.0.0.1:50973",
        "el": "http://127.0.0.1:50965"
      }
    ],
    "addresses": {
      "delayedWETHImpl": "0x6cc791cd89220c47952885782156e5e8f2833063",
      "disputeGameFactoryImpl": "0x256fa44786f5b9aa47f13639b97a52cbbb8d3b6e",
      "l1CrossDomainMessengerImpl": "0xe70dae11d895c2e508e7d1c0c91dd0f3e4e971c3",
      "l1ERC721BridgeImpl": "0x5a0242103310c7992773430e790c142b7e94ef3a",
      "l1StandardBridgeImpl": "0x8eb57e8ca6e1e3216b4293cc37d621a43c17ec40",
      "mipsSingleton": "0xb5322da185af39b53f8d3cbf40636eb3f1b527f2",
      "opcm": "0xf0f6e276962a776112511c4ea74ad12b4e44cd6b",
      "optimismMintableERC20FactoryImpl": "0x5f554a3eabcc8e2055040d563ef4910e8bacff8d",
      "optimismPortalImpl": "0x9d1da1fef40e2b5f44ed193bd88f1d3ee4a31cc5",
      "preimageOracleSingleton": "0x31502454e4b07ab6f35216fd734aafd816a06110",
      "protocolVersionsImpl": "0xba337ce3e690fdd01b1a830404e8caaae22a0638",
      "protocolVersionsProxy": "0xf2749e312b43529cb35a91e355919a376bd22ce3",
      "proxyAdmin": "0xed3b4bf7e17e47dac37d7bb913196e192dcaf748",
      "superchainConfigImpl": "0xcf99c17529cc22c39551547e323ed9dda6841734",
      "superchainConfigProxy": "0x329cf899464ba580bf047f4b886db342c602f579",
      "systemConfigImpl": "0x5d58414e85fc12d343eb3445dd7417c483d939a9"
    }
  },
...
parent c7a6819a
...@@ -171,11 +171,15 @@ func (d *KurtosisDeployer) getEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -171,11 +171,15 @@ func (d *KurtosisDeployer) getEnvironmentInfo(ctx context.Context, spec *spec.En
// Find L1 endpoint // Find L1 endpoint
finder := NewServiceFinder(inspectResult.UserServices) finder := NewServiceFinder(inspectResult.UserServices)
if nodes, endpoints := finder.FindL1Endpoints(); len(nodes) > 0 { if nodes, endpoints := finder.FindL1Endpoints(); len(nodes) > 0 {
env.L1 = &Chain{ chain := &Chain{
Name: "Ethereum", Name: "Ethereum",
Services: endpoints, Services: endpoints,
Nodes: nodes, Nodes: nodes,
} }
if deployerState.State != nil {
chain.Addresses = deployerState.State.Addresses
}
env.L1 = chain
} }
// Find L2 endpoints // Find L2 endpoints
...@@ -190,9 +194,11 @@ func (d *KurtosisDeployer) getEnvironmentInfo(ctx context.Context, spec *spec.En ...@@ -190,9 +194,11 @@ func (d *KurtosisDeployer) getEnvironmentInfo(ctx context.Context, spec *spec.En
} }
// Add contract addresses if available // Add contract addresses if available
if addresses, ok := deployerState.State[chainSpec.NetworkID]; ok { if deployerState.State != nil && deployerState.State.Deployments != nil {
if addresses, ok := deployerState.State.Deployments[chainSpec.NetworkID]; ok {
chain.Addresses = addresses chain.Addresses = addresses
} }
}
env.L2 = append(env.L2, chain) env.L2 = append(env.L2, chain)
} }
......
...@@ -25,24 +25,31 @@ type DeploymentAddresses map[string]string ...@@ -25,24 +25,31 @@ type DeploymentAddresses map[string]string
// DeploymentStateAddresses maps chain IDs to their contract addresses // DeploymentStateAddresses maps chain IDs to their contract addresses
type DeploymentStateAddresses map[string]DeploymentAddresses type DeploymentStateAddresses map[string]DeploymentAddresses
type DeployerState struct {
Deployments DeploymentStateAddresses `json:"l2s"`
Addresses DeploymentAddresses `json:"superchain"`
}
// StateFile represents the structure of the state.json file // StateFile represents the structure of the state.json file
type StateFile struct { type StateFile struct {
OpChainDeployments []map[string]interface{} `json:"opChainDeployments"` OpChainDeployments []map[string]interface{} `json:"opChainDeployments"`
SuperChainDeployment map[string]interface{} `json:"superchainDeployment"`
ImplementationsDeployment map[string]interface{} `json:"implementationsDeployment"`
} }
// Wallet represents a wallet with optional private key and name // Wallet represents a wallet with optional private key and name
type Wallet struct { type Wallet struct {
Address string Address string `json:"address"`
PrivateKey string PrivateKey string `json:"private_key"`
Name string Name string `json:"name"`
} }
// WalletList holds a list of wallets // WalletList holds a list of wallets
type WalletList []*Wallet type WalletList []*Wallet
type DeployerData struct { type DeployerData struct {
Wallets WalletList Wallets WalletList `json:"wallets"`
State DeploymentStateAddresses State *DeployerState `json:"state"`
} }
type Deployer struct { type Deployer struct {
...@@ -176,13 +183,27 @@ func hexToDecimal(hex string) (string, error) { ...@@ -176,13 +183,27 @@ func hexToDecimal(hex string) (string, error) {
} }
// parseStateFile parses the state.json file and extracts addresses // parseStateFile parses the state.json file and extracts addresses
func parseStateFile(r io.Reader) (DeploymentStateAddresses, error) { func parseStateFile(r io.Reader) (*DeployerState, error) {
var state StateFile var state StateFile
if err := json.NewDecoder(r).Decode(&state); err != nil { if err := json.NewDecoder(r).Decode(&state); err != nil {
return nil, fmt.Errorf("failed to decode state file: %w", err) return nil, fmt.Errorf("failed to decode state file: %w", err)
} }
result := make(DeploymentStateAddresses) result := &DeployerState{
Deployments: make(DeploymentStateAddresses),
Addresses: make(DeploymentAddresses),
}
mapDeployment := func(deployment map[string]interface{}) DeploymentAddresses {
addrSuffix := "Address"
addresses := make(DeploymentAddresses)
for key, value := range deployment {
if strings.HasSuffix(key, addrSuffix) {
addresses[strings.TrimSuffix(key, addrSuffix)] = value.(string)
}
}
return addresses
}
for _, deployment := range state.OpChainDeployments { for _, deployment := range state.OpChainDeployments {
// Get the chain ID // Get the chain ID
...@@ -201,19 +222,17 @@ func parseStateFile(r io.Reader) (DeploymentStateAddresses, error) { ...@@ -201,19 +222,17 @@ func parseStateFile(r io.Reader) (DeploymentStateAddresses, error) {
continue continue
} }
addresses := make(DeploymentAddresses) addresses := mapDeployment(deployment)
// Look for address fields in the deployment map if len(addresses) > 0 {
for key, value := range deployment { result.Deployments[id] = addresses
if strings.HasSuffix(key, "Address") {
key = strings.TrimSuffix(key, "Address")
addresses[key] = value.(string)
} }
} }
if len(addresses) > 0 { result.Addresses = mapDeployment(state.ImplementationsDeployment)
result[id] = addresses // merge the superchain and implementations addresses
} for key, value := range mapDeployment(state.SuperChainDeployment) {
result.Addresses[key] = value
} }
return result, nil return result, nil
......
...@@ -25,12 +25,21 @@ func TestParseStateFile(t *testing.T) { ...@@ -25,12 +25,21 @@ func TestParseStateFile(t *testing.T) {
"someOtherField": 123, "someOtherField": 123,
"L2OutputOracleAddress": "0xghi" "L2OutputOracleAddress": "0xghi"
} }
] ],
"superchainDeployment": {
"SuperchainConfigAddress": "0x111",
"ProtocolVersionsAddress": "0x222"
},
"implementationsDeployment": {
"L1CrossDomainMessengerProxyAddress": "0x333",
"L1StandardBridgeProxyAddress": "0x444"
}
}` }`
result, err := parseStateFile(strings.NewReader(stateJSON)) result, err := parseStateFile(strings.NewReader(stateJSON))
require.NoError(t, err, "Failed to parse state file") require.NoError(t, err, "Failed to parse state file")
// Test chain deployments
tests := []struct { tests := []struct {
chainID string chainID string
expected DeploymentAddresses expected DeploymentAddresses
...@@ -54,7 +63,7 @@ func TestParseStateFile(t *testing.T) { ...@@ -54,7 +63,7 @@ func TestParseStateFile(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
chain, ok := result[tt.chainID] chain, ok := result.Deployments[tt.chainID]
require.True(t, ok, "Chain %s not found in result", tt.chainID) require.True(t, ok, "Chain %s not found in result", tt.chainID)
for key, expected := range tt.expected { for key, expected := range tt.expected {
...@@ -62,6 +71,20 @@ func TestParseStateFile(t *testing.T) { ...@@ -62,6 +71,20 @@ func TestParseStateFile(t *testing.T) {
require.Equal(t, expected, actual, "Chain %s, %s: expected %s, got %s", tt.chainID, key, expected, actual) require.Equal(t, expected, actual, "Chain %s, %s: expected %s, got %s", tt.chainID, key, expected, actual)
} }
} }
// Test superchain and implementations addresses
expectedAddresses := DeploymentAddresses{
"SuperchainConfig": "0x111",
"ProtocolVersions": "0x222",
"L1CrossDomainMessengerProxy": "0x333",
"L1StandardBridgeProxy": "0x444",
}
for key, expected := range expectedAddresses {
actual, ok := result.Addresses[key]
require.True(t, ok, "Address %s not found in result", key)
require.Equal(t, expected, actual, "Address %s: expected %s, got %s", key, expected, actual)
}
} }
func TestParseStateFileErrors(t *testing.T) { func TestParseStateFileErrors(t *testing.T) {
......
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