Skip to content

Commit 456e71a

Browse files
authored
Generate deterministic enode addresses (#146)
1 parent 4eb2e87 commit 456e71a

File tree

5 files changed

+80
-32
lines changed

5 files changed

+80
-32
lines changed

internal/artifacts.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/ethereum/go-ethereum/common/hexutil"
2727
"github.com/ethereum/go-ethereum/core/types"
2828
ecrypto "github.com/ethereum/go-ethereum/crypto"
29+
"github.com/ethereum/go-ethereum/p2p/enode"
2930
"github.com/hashicorp/go-uuid"
3031
"github.com/prysmaticlabs/prysm/v5/config/params"
3132
"github.com/prysmaticlabs/prysm/v5/crypto/bls/common"
@@ -37,8 +38,6 @@ import (
3738

3839
var (
3940
defaultOpBlockTimeSeconds = uint64(2)
40-
defaultDiscoveryPrivKey = "a11ac89899cd86e36b6fb881ec1255b8a92a688790b7d950f8b7d8dd626671fb"
41-
defaultDiscoveryEnodeID = "3479db4d9217fb5d7a8ed4d61ac36e120b05d36c2eefb795dc42ff2e971f251a2315f5649ea1833271e020b9adc98d5db9973c7ed92d6b2f1f2223088c3d852f"
4241
)
4342

4443
// minimumGenesisDelay is the minimum delay for the genesis time. This is required
@@ -246,7 +245,6 @@ func (b *ArtifactsBuilder) Build() (*Artifacts, error) {
246245
"testnet/deposit_contract_block.txt": "0",
247246
"testnet/genesis_validators_root.txt": hex.EncodeToString(state.GenesisValidatorsRoot()),
248247
"data_validator/": &lighthouseKeystore{privKeys: priv},
249-
"deterministic_p2p_key.txt": defaultDiscoveryPrivKey,
250248
"scripts/query.sh": queryReadyCheck,
251249
})
252250
if err != nil {
@@ -418,6 +416,8 @@ type output struct {
418416

419417
homeDir string
420418
lock sync.Mutex
419+
420+
enodeAddrSeq *big.Int
421421
}
422422

423423
func (o *output) AbsoluteDstPath() (string, error) {
@@ -696,3 +696,38 @@ func applyTemplate2(templateStr []byte, input interface{}) ([]byte, error) {
696696

697697
return []byte(out.String()), nil
698698
}
699+
700+
type EnodeAddr struct {
701+
PrivKey *ecdsa.PrivateKey
702+
Artifact string
703+
}
704+
705+
func (e *EnodeAddr) ID() enode.ID {
706+
return enode.PubkeyToIDV4(&e.PrivKey.PublicKey)
707+
}
708+
709+
func (o *output) GetEnodeAddr() *EnodeAddr {
710+
// TODO: This is a bit enshrined here
711+
if o.enodeAddrSeq == nil {
712+
o.enodeAddrSeq = big.NewInt(0)
713+
}
714+
715+
// always start with 1 since 0 is not a valid private key for an enode address
716+
o.enodeAddrSeq.Add(o.enodeAddrSeq, big.NewInt(1))
717+
privKeyBytes := gethcommon.LeftPadBytes(o.enodeAddrSeq.Bytes(), 32)
718+
719+
privKey, err := ecrypto.ToECDSA(privKeyBytes)
720+
if err != nil {
721+
panic(fmt.Sprintf("BUG: failed to convert private key to ECDSA: %v", err))
722+
}
723+
724+
privKeyBytesHex := hex.EncodeToString(privKeyBytes)
725+
726+
// write the key to an artifact file
727+
fileName := fmt.Sprintf("enode-key-%d.txt", o.enodeAddrSeq.Int64())
728+
if err := o.WriteFile(fileName, privKeyBytesHex); err != nil {
729+
panic(fmt.Sprintf("BUG: failed to write enode key to artifact file: %v", err))
730+
}
731+
732+
return &EnodeAddr{PrivKey: privKey, Artifact: fileName}
733+
}

internal/artifacts_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package internal
2+
3+
import (
4+
"os"
5+
"testing"
6+
)
7+
8+
func TestEnodeGeneration(t *testing.T) {
9+
// Test that the enodes generated by output are deterministic
10+
o1 := newTestOutput(t)
11+
o2 := newTestOutput(t)
12+
13+
for i := 0; i < 10; i++ {
14+
if o1.GetEnodeAddr().ID() != o2.GetEnodeAddr().ID() {
15+
t.Fatalf("enode IDs are not the same")
16+
}
17+
}
18+
}
19+
20+
func newTestOutput(t *testing.T) *output {
21+
dir, err := os.MkdirTemp("/tmp", "test-output")
22+
if err != nil {
23+
t.Fatalf("failed to create temporal folder: %v", err)
24+
}
25+
defer os.RemoveAll(dir)
26+
27+
o := &output{
28+
dst: dir,
29+
}
30+
return o
31+
}

internal/components.go

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"io"
77
"strconv"
8-
"strings"
98
"time"
109
)
1110

@@ -112,10 +111,8 @@ func (o *OpNode) Name() string {
112111
}
113112

114113
type OpGeth struct {
115-
UseDeterministicP2PKey bool
116-
117114
// outputs
118-
Enode string
115+
Enode *EnodeAddr
119116
}
120117

121118
func logLevelToGethVerbosity(logLevel LogLevel) string {
@@ -136,10 +133,7 @@ func logLevelToGethVerbosity(logLevel LogLevel) string {
136133
}
137134

138135
func (o *OpGeth) Run(service *Service, ctx *ExContext) {
139-
var nodeKeyFlag string
140-
if o.UseDeterministicP2PKey {
141-
nodeKeyFlag = "--nodekey /data/deterministic_p2p_key.txt "
142-
}
136+
o.Enode = ctx.Output.GetEnodeAddr()
143137

144138
service.
145139
WithImage("us-docker.pkg.dev/oplabs-tools-artifacts/images/op-geth").
@@ -174,37 +168,21 @@ func (o *OpGeth) Run(service *Service, ctx *ExContext) {
174168
"--gcmode archive "+
175169
"--state.scheme hash "+
176170
"--port "+`{{Port "rpc" 30303}} `+
177-
nodeKeyFlag+
171+
"--nodekey /data/p2p_key.txt "+
178172
"--metrics "+
179173
"--metrics.addr 0.0.0.0 "+
180174
"--metrics.port "+`{{Port "metrics" 6061}}`,
181175
).
182176
WithVolume("data", "/data_opgeth").
183177
WithArtifact("/data/l2-genesis.json", "l2-genesis.json").
184178
WithArtifact("/data/jwtsecret", "jwtsecret").
185-
WithArtifact("/data/deterministic_p2p_key.txt", "deterministic_p2p_key.txt")
179+
WithArtifact("/data/p2p_key.txt", o.Enode.Artifact)
186180
}
187181

188182
func (o *OpGeth) Name() string {
189183
return "op-geth"
190184
}
191185

192-
var _ ServiceReady = &OpGeth{}
193-
194-
func (o *OpGeth) Ready(instance *instance) error {
195-
enodeLine, err := instance.logs.FindLog("enode://")
196-
if err != nil {
197-
return err
198-
}
199-
200-
parts := strings.Split(enodeLine, "enode://")[1]
201-
enodeID := strings.Split(parts, "@")[0]
202-
203-
enode := fmt.Sprintf("enode://%[email protected]:%d?discport=0", enodeID, instance.service.MustGetPort("rpc").HostPort)
204-
o.Enode = enode
205-
return nil
206-
}
207-
208186
var _ ServiceWatchdog = &OpGeth{}
209187

210188
func (o *OpGeth) Watchdog(out io.Writer, instance *instance, ctx context.Context) error {

internal/manifest.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type Manifest struct {
3737
}
3838

3939
func NewManifest(ctx *ExContext, out *output) *Manifest {
40+
ctx.Output = out
4041
return &Manifest{ctx: ctx, out: out, overrides: make(map[string]string)}
4142
}
4243

@@ -71,6 +72,11 @@ func (l *LogLevel) Unmarshal(s string) error {
7172
// Execution context
7273
type ExContext struct {
7374
LogLevel LogLevel
75+
76+
// This dependency is not ideal. Doing it so that I do not
77+
// have to modify the serviceDesc interface to give services
78+
// access to the output.
79+
Output *output
7480
}
7581

7682
type ServiceGen interface {

internal/recipe_opstack.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,7 @@ func (o *OpRecipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
8080
L1Beacon: "beacon",
8181
L2Node: elNode,
8282
})
83-
svcManager.AddService("op-geth", &OpGeth{
84-
UseDeterministicP2PKey: o.externalBuilder != "",
85-
})
83+
svcManager.AddService("op-geth", &OpGeth{})
8684
svcManager.AddService("op-batcher", &OpBatcher{
8785
L1Node: "el",
8886
L2Node: "op-geth",

0 commit comments

Comments
 (0)