Skip to content

Commit b5cec7b

Browse files
authored
Merge branch 'main' into jihwan/silence-ulxly-help
2 parents 453c8eb + 2e2cf41 commit b5cec7b

18 files changed

+455
-154
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tmp/

Dockerfile

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,25 @@
1-
FROM golang:1.22 AS builder
1+
# Build stage
2+
FROM --platform=${BUILDPLATFORM} golang:1.22 AS builder
3+
4+
# Set the workspace for the build
25
WORKDIR /workspace
6+
7+
# Copy only necessary Go module files for caching dependencies
38
COPY go.mod go.sum ./
49
RUN go mod download
510

6-
COPY abi/ abi/
7-
COPY bindings/ bindings/
8-
COPY cmd/ cmd/
9-
COPY dashboard/ dashboard/
10-
COPY gethkeystore/ gethkeystore/
11-
COPY hdwallet/ hdwallet/
12-
COPY metrics/ metrics/
13-
COPY p2p/ p2p/
14-
COPY proto/ proto/
15-
COPY rpctypes/ rpctypes/
16-
COPY util/ util/
17-
COPY main.go ./
18-
RUN CGO_ENABLED=0 go build -o polycli main.go
11+
# Copy the necessary source code
12+
COPY . ./
13+
14+
# Build the Go binary
15+
RUN go build -o /workspace/polycli main.go
16+
17+
# Final stage: minimal base image
18+
FROM --platform=${BUILDPLATFORM} debian:bookworm-slim
1919

20-
# Use distroless as minimal base image to package the manager binary
21-
# Refer to https://github.com/GoogleContainerTools/distroless for more details
22-
FROM gcr.io/distroless/static:nonroot
23-
WORKDIR /
20+
# Copy only the necessary files from the builder image
2421
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
2522
COPY --from=builder /workspace/polycli /usr/bin/polycli
26-
USER 65532:65532
27-
ENTRYPOINT ["polycli"]
28-
CMD ["--help"]
2923

30-
# How to test this image?
31-
# https://github.com/0xPolygon/polygon-cli/pull/189#discussion_r1464486344
24+
# Default cmd for the container
25+
ENTRYPOINT ["polycli"]

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ $(BUILD_DIR): ## Create the build folder.
2929
build: $(BUILD_DIR) ## Build go binary.
3030
go build -ldflags "$(VERSION_FLAGS)" -o $(BUILD_DIR)/$(BIN_NAME) main.go
3131

32+
.PHONY: build-docker
33+
build-docker: ## Builds a docker image with the polycli binary
34+
docker build -t polycli -f ./Dockerfile .
35+
3236
.PHONY: install
3337
install: build ## Install the go binary.
3438
$(RM) $(INSTALL_DIR)/$(BIN_NAME)

cmd/fund/fund.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func deployOrInstantiateFunderContract(ctx context.Context, c *ethclient.Client,
158158
return contract, nil
159159
}
160160

161-
// deriveWallets generates and exports a specified number of HD wallet addresses.
161+
// deriveHDWallets generates and exports a specified number of HD wallet addresses.
162162
func deriveHDWallets(n int) ([]common.Address, error) {
163163
wallet, err := hdwallet.NewPolyWallet(defaultMnemonic, defaultPassword)
164164
if err != nil {

cmd/nodekey/nodekey.go

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package nodekey
22

33
import (
44
"bytes"
5+
"crypto/ecdsa"
56
"crypto/ed25519"
67
"crypto/rand"
78
"encoding/binary"
@@ -10,6 +11,7 @@ import (
1011
"fmt"
1112
"io"
1213
"net"
14+
"strings"
1315

1416
_ "embed"
1517

@@ -42,6 +44,7 @@ var (
4244
inputNodeKeyTCP *int
4345
inputNodeKeyUDP *int
4446
inputNodeKeyFile *string
47+
inputNodeKeyPrivateKey *string
4548
inputNodeKeySign *bool
4649
inputNodeKeySeed *uint64
4750
inputNodeKeyMarshalProtobuf *bool
@@ -67,11 +70,22 @@ var NodekeyCmd = &cobra.Command{
6770
var withSeed bool
6871
switch *inputNodeKeyProtocol {
6972
case "devp2p":
70-
var err error
71-
nko, err = generateDevp2pNodeKey()
72-
if err != nil {
73-
return err
73+
switch *inputNodeKeyType {
74+
case "ed25519":
75+
var err error
76+
nko, err = generateDevp2pNodeKey()
77+
if err != nil {
78+
return err
79+
}
80+
case "secp256k1":
81+
secret := []byte(strings.TrimPrefix(*inputNodeKeyPrivateKey, "0x"))
82+
secp256k1PrivateKey := generateSecp256k1PrivateKey(secret)
83+
if err := displayHeimdallV2PrivValidatorKey(secp256k1PrivateKey); err != nil {
84+
return err
85+
}
86+
return nil
7487
}
88+
7589
case "seed-libp2p":
7690
withSeed = true
7791
fallthrough
@@ -100,14 +114,15 @@ var NodekeyCmd = &cobra.Command{
100114
if len(args) != 0 {
101115
return fmt.Errorf("this command expects no arguments")
102116
}
117+
103118
validProtocols := []string{"devp2p", "libp2p", "seed-libp2p"}
104119
ok := slices.Contains(validProtocols, *inputNodeKeyProtocol)
105120
if !ok {
106121
return fmt.Errorf("the protocol %s is not implemented", *inputNodeKeyProtocol)
107122
}
108123

109124
if *inputNodeKeyProtocol == "devp2p" {
110-
invalidFlags := []string{"key-type", "seed", "marshal-protobuf"}
125+
invalidFlags := []string{"seed", "marshal-protobuf"}
111126
err := validateNodeKeyFlags(cmd, invalidFlags)
112127
if err != nil {
113128
return err
@@ -169,13 +184,26 @@ func keyTypeToInt(keyType string) (int, error) {
169184
}
170185

171186
func generateDevp2pNodeKey() (nodeKeyOut, error) {
172-
nodeKey, err := gethcrypto.GenerateKey()
187+
var nodeKey *ecdsa.PrivateKey
188+
var err error
173189

174-
if *inputNodeKeyFile != "" {
190+
switch {
191+
case *inputNodeKeyPrivateKey != "":
192+
privateKey := strings.TrimPrefix(*inputNodeKeyPrivateKey, "0x")
193+
nodeKey, err = gethcrypto.HexToECDSA(privateKey)
194+
if err != nil {
195+
return nodeKeyOut{}, fmt.Errorf("could not create ECDSA private key from given value: %s: %w", *inputNodeKeyPrivateKey, err)
196+
}
197+
case *inputNodeKeyFile != "":
175198
nodeKey, err = gethcrypto.LoadECDSA(*inputNodeKeyFile)
176-
}
177-
if err != nil {
178-
return nodeKeyOut{}, fmt.Errorf("could not generate key: %w", err)
199+
if err != nil {
200+
return nodeKeyOut{}, fmt.Errorf("could not load ECDSA private key from file %s: %w", *inputNodeKeyFile, err)
201+
}
202+
default:
203+
nodeKey, err = gethcrypto.GenerateKey()
204+
if err != nil {
205+
return nodeKeyOut{}, fmt.Errorf("could not generate ECDSA private key: %w", err)
206+
}
179207
}
180208

181209
nko := nodeKeyOut{}
@@ -250,6 +278,10 @@ func generateLibp2pNodeKey(keyType int, seed bool) (nodeKeyOut, error) {
250278
}
251279

252280
func init() {
281+
inputNodeKeyPrivateKey = NodekeyCmd.PersistentFlags().String("private-key", "", "Use the provided private key (in hex format)")
282+
inputNodeKeyFile = NodekeyCmd.PersistentFlags().StringP("file", "f", "", "A file with the private nodekey (in hex format)")
283+
NodekeyCmd.MarkFlagsMutuallyExclusive("private-key", "file")
284+
253285
inputNodeKeyProtocol = NodekeyCmd.PersistentFlags().String("protocol", "devp2p", "devp2p|libp2p|pex|seed-libp2p")
254286
inputNodeKeyType = NodekeyCmd.PersistentFlags().String("key-type", "ed25519", "ed25519|secp256k1|ecdsa|rsa")
255287
inputNodeKeyIP = NodekeyCmd.PersistentFlags().StringP("ip", "i", "0.0.0.0", "The IP to be associated with this address")
@@ -258,6 +290,4 @@ func init() {
258290
inputNodeKeySign = NodekeyCmd.PersistentFlags().BoolP("sign", "s", false, "Should the node record be signed?")
259291
inputNodeKeySeed = NodekeyCmd.PersistentFlags().Uint64P("seed", "S", 271828, "A numeric seed value")
260292
inputNodeKeyMarshalProtobuf = NodekeyCmd.PersistentFlags().BoolP("marshal-protobuf", "m", false, "If true the libp2p key will be marshaled to protobuf format rather than raw")
261-
262-
inputNodeKeyFile = NodekeyCmd.PersistentFlags().StringP("file", "f", "", "A file with the private nodekey in hex format")
263293
}

cmd/nodekey/secp256k1.go

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package nodekey
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"crypto/subtle"
7+
"fmt"
8+
"math/big"
9+
10+
secp256k1 "github.com/btcsuite/btcd/btcec/v2"
11+
"github.com/cometbft/cometbft/crypto"
12+
"github.com/cometbft/cometbft/privval"
13+
14+
cmtjson "github.com/cometbft/cometbft/libs/json"
15+
gethcrypto "github.com/ethereum/go-ethereum/crypto"
16+
)
17+
18+
const (
19+
PrivKeyName = "comet/PrivKeySecp256k1Uncompressed"
20+
PubKeyName = "comet/PubKeySecp256k1Uncompressed"
21+
22+
KeyType = "secp256k1"
23+
PrivKeySize = 32
24+
// PubKeySize (uncompressed) is composed of 65 bytes for two field elements (x and y)
25+
// and a prefix byte (0x04) to indicate that it is uncompressed.
26+
PubKeySize = 65
27+
// SigSize is the size of the ECDSA signature.
28+
SigSize = 65
29+
)
30+
31+
var _ crypto.PrivKey = PrivKey{}
32+
var _ crypto.PubKey = PubKey{}
33+
34+
// -------------------------------------
35+
// PrivKey type
36+
// -------------------------------------
37+
38+
type PrivKey []byte
39+
40+
// Bytes marshals the private key using amino encoding.
41+
func (privKey PrivKey) Bytes() []byte {
42+
return []byte(privKey)
43+
}
44+
45+
// PubKey performs the point-scalar multiplication from the privKey on the
46+
// generator point to get the pubkey.
47+
func (privKey PrivKey) PubKey() crypto.PubKey {
48+
privateObject, err := gethcrypto.ToECDSA(privKey)
49+
if err != nil {
50+
panic(err)
51+
}
52+
53+
pk := gethcrypto.FromECDSAPub(&privateObject.PublicKey)
54+
55+
return PubKey(pk)
56+
}
57+
58+
// Equals - you probably don't need to use this.
59+
// Runs in constant time based on length of the keys.
60+
func (privKey PrivKey) Equals(other crypto.PrivKey) bool {
61+
if otherSecp, ok := other.(PrivKey); ok {
62+
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1
63+
}
64+
return false
65+
}
66+
67+
func (privKey PrivKey) Type() string {
68+
return KeyType
69+
}
70+
71+
// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg.
72+
// The returned signature will be of the form R || S || V (in lower-S form).
73+
func (privKey PrivKey) Sign(msg []byte) ([]byte, error) {
74+
privateObject, err := gethcrypto.ToECDSA(privKey)
75+
if err != nil {
76+
return nil, err
77+
}
78+
79+
return gethcrypto.Sign(gethcrypto.Keccak256(msg), privateObject)
80+
}
81+
82+
// -------------------------------------
83+
// PubKey type
84+
// -------------------------------------
85+
86+
type PubKey []byte
87+
88+
// Bytes returns the pubkey marshaled with amino encoding.
89+
func (pubKey PubKey) Bytes() []byte {
90+
return []byte(pubKey)
91+
}
92+
93+
// Address returns a Ethereym style addresses: Last_20_Bytes(KECCAK256(pubkey))
94+
func (pubKey PubKey) Address() crypto.Address {
95+
if len(pubKey) != PubKeySize {
96+
panic(fmt.Sprintf("length of pubkey is incorrect %d != %d", len(pubKey), PubKeySize))
97+
}
98+
return gethcrypto.Keccak256(pubKey[1:])[12:]
99+
}
100+
101+
func (pubKey PubKey) Equals(other crypto.PubKey) bool {
102+
if otherSecp, ok := other.(PubKey); ok {
103+
return bytes.Equal(pubKey[:], otherSecp[:])
104+
}
105+
return false
106+
}
107+
108+
func (pubKey PubKey) Type() string {
109+
return KeyType
110+
}
111+
112+
// VerifySignature verifies a signature of the form R || S || V.
113+
// It rejects signatures which are not in lower-S form.
114+
func (pubKey PubKey) VerifySignature(msg []byte, sigStr []byte) bool {
115+
if len(sigStr) != SigSize {
116+
117+
return false
118+
}
119+
120+
hash := gethcrypto.Keccak256(msg)
121+
return gethcrypto.VerifySignature(pubKey, hash, sigStr[:64])
122+
}
123+
124+
func init() {
125+
cmtjson.RegisterType(PubKey{}, PubKeyName)
126+
cmtjson.RegisterType(PrivKey{}, PrivKeyName)
127+
}
128+
129+
// Generate an secp256k1 private key from a secret.
130+
// Most of the logic has been copy/pasted from 0xPolygon/cometbft's fork.
131+
// https://github.com/0xPolygon/cometbft/blob/v0.1.2-beta-polygon/crypto/secp256k1/secp256k1.go
132+
// Notes:
133+
// - It is not possible to import the package yet because go.mod declares its path as github.com/cometbft/cometbft instead of github.com/0xpolygon/cometbft.
134+
// - This logic will need to be updated to support newer versions.
135+
func generateSecp256k1PrivateKey(secret []byte) PrivKey {
136+
// To guarantee that we have a valid field element, we use the approach of: "Suite B Implementer’s Guide to FIPS 186-3", A.2.1
137+
// https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm
138+
// See also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101
139+
secretHash := sha256.Sum256(secret)
140+
fe := new(big.Int).SetBytes(secretHash[:])
141+
142+
one := new(big.Int).SetInt64(1)
143+
n := new(big.Int).Sub(secp256k1.S256().N, one)
144+
fe.Mod(fe, n)
145+
fe.Add(fe, one)
146+
147+
feB := fe.Bytes()
148+
privKey32 := make([]byte, PrivKeySize)
149+
// Copy feB over to fixed 32 byte privKey32 and pad (if necessary).
150+
copy(privKey32[32-len(feB):32], feB)
151+
152+
return PrivKey(privKey32)
153+
}
154+
155+
func displayHeimdallV2PrivValidatorKey(privKey crypto.PrivKey) error {
156+
nodeKey := privval.FilePVKey{
157+
Address: privKey.PubKey().Address(),
158+
PubKey: privKey.PubKey(),
159+
PrivKey: privKey,
160+
}
161+
jsonBytes, err := cmtjson.MarshalIndent(nodeKey, "", " ")
162+
if err != nil {
163+
return err
164+
}
165+
fmt.Println(string(jsonBytes))
166+
return nil
167+
}

cmd/nodekey/usage.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,37 @@ $ polycli nodekey --protocol libp2p
1515
# Generate a networking keypair for edge.
1616
$ polycli nodekey --protocol libp2p --key-type secp256k1 --marshal-protobuf
1717
```
18+
19+
Generate an [ED25519](https://en.wikipedia.org/wiki/Curve25519) nodekey from a private key (in hex format).
20+
21+
```bash
22+
polycli nodekey --private-key 2a4ae8c4c250917781d38d95dafbb0abe87ae2c9aea02ed7c7524685358e49c2 | jq
23+
```
24+
25+
```json
26+
{
27+
"PublicKey": "93e8717f46b146ebfb99159eb13a5d044c191998656c8b79007b16051bb1ff762d09884e43783d898dd47f6220af040206cabbd45c9a26bb278a522c3d538a1f",
28+
"PrivateKey": "2a4ae8c4c250917781d38d95dafbb0abe87ae2c9aea02ed7c7524685358e49c2",
29+
"ENR": "enode://93e8717f46b146ebfb99159eb13a5d044c191998656c8b79007b16051bb1ff762d09884e43783d898dd47f6220af040206cabbd45c9a26bb278a522c3d538a1f@0.0.0.0:30303?discport=0"
30+
}
31+
```
32+
33+
Generate an [Secp256k1](https://en.bitcoin.it/wiki/Secp256k1) nodekey from a private key (in hex format).
34+
35+
```bash
36+
polycli nodekey --private-key 2a4ae8c4c250917781d38d95dafbb0abe87ae2c9aea02ed7c7524685358e49c2 --key-type secp256k1 | jq
37+
```
38+
39+
```json
40+
{
41+
"address": "99AA9FC116C1E5E741E9EC18BD1FD232130A5C44",
42+
"pub_key": {
43+
"type": "comet/PubKeySecp256k1Uncompressed",
44+
"value": "BBNYN0nMJsgo0Fp3kVW85PRGBNe7Gdz1XBFuTWQ7D8FnKRb2JYO3i3FK2UiA5+gTSxYu1K66KdYjQYP1mOkH09g="
45+
},
46+
"priv_key": {
47+
"type": "comet/PrivKeySecp256k1Uncompressed",
48+
"value": "OP72E0D7GEi/4VySpolVudLW7uPJm+6PWEtFKJmvp1M="
49+
}
50+
}
51+
```

0 commit comments

Comments
 (0)