bridge: implement bridge key serialization
ghstack-source-id: f218021514618ae1eb8f03d7cc158b1114c45297 Pull Request resolved: https://github.com/certusone/wormhole/pull/90
This commit is contained in:
parent
114524a096
commit
d9f8174d76
|
@ -9,6 +9,7 @@ import (
|
|||
"syscall"
|
||||
|
||||
eth_common "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/libp2p/go-libp2p-core/crypto"
|
||||
"github.com/libp2p/go-libp2p-core/peer"
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -37,6 +38,8 @@ var (
|
|||
|
||||
nodeKeyPath *string
|
||||
|
||||
bridgeKeyPath *string
|
||||
|
||||
ethRPC *string
|
||||
ethContract *string
|
||||
ethConfirmations *uint64
|
||||
|
@ -64,6 +67,8 @@ func init() {
|
|||
|
||||
nodeKeyPath = BridgeCmd.Flags().String("nodeKey", "", "Path to node key (will be generated if it doesn't exist)")
|
||||
|
||||
bridgeKeyPath = BridgeCmd.Flags().String("bridgeKey", "", "Path to guardian key (required)")
|
||||
|
||||
ethRPC = BridgeCmd.Flags().String("ethRPC", "", "Ethereum RPC URL")
|
||||
ethContract = BridgeCmd.Flags().String("ethContract", "", "Ethereum bridge contract address")
|
||||
ethConfirmations = BridgeCmd.Flags().Uint64("ethConfirmations", 15, "Ethereum confirmation count requirement")
|
||||
|
@ -184,6 +189,9 @@ func runBridge(cmd *cobra.Command, args []string) {
|
|||
if *nodeKeyPath == "" && !*unsafeDevMode { // In devnet mode, keys are deterministically generated.
|
||||
logger.Fatal("Please specify -nodeKey")
|
||||
}
|
||||
if *bridgeKeyPath == "" {
|
||||
logger.Fatal("Please specify -bridgeKey")
|
||||
}
|
||||
if *agentRPC == "" {
|
||||
logger.Fatal("Please specify -agentRPC")
|
||||
}
|
||||
|
@ -216,8 +224,27 @@ func runBridge(cmd *cobra.Command, args []string) {
|
|||
|
||||
ethContractAddr := eth_common.HexToAddress(*ethContract)
|
||||
|
||||
// In devnet mode, we generate a deterministic guardian key and write it to disk.
|
||||
if *unsafeDevMode {
|
||||
gk, err := generateDevnetGuardianKey()
|
||||
if err != nil {
|
||||
logger.Fatal("failed to generate devnet guardian key", zap.Error(err))
|
||||
}
|
||||
|
||||
err = writeGuardianKey(gk, "auto-generated deterministic devnet key", *bridgeKeyPath)
|
||||
if err != nil {
|
||||
logger.Fatal("failed to write devnet guardian key", zap.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
// Guardian key
|
||||
gk := loadGuardianKey(logger)
|
||||
gk, err := loadGuardianKey(*bridgeKeyPath)
|
||||
if err != nil {
|
||||
logger.Fatal("failed to load guardian key", zap.Error(err))
|
||||
}
|
||||
|
||||
logger.Info("Loaded guardian key", zap.String(
|
||||
"address", ethcrypto.PubkeyToAddress(gk.PublicKey).String()))
|
||||
|
||||
// Node's main lifecycle context.
|
||||
rootCtx, rootCtxCancel = context.WithCancel(context.Background())
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package guardiand
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"google.golang.org/protobuf/encoding/prototext"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1"
|
||||
)
|
||||
|
||||
// loadGuardianKey loads a serialized guardian key from disk.
|
||||
func loadGuardianKey(filename string) (*ecdsa.PrivateKey, error) {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read guardian private key from disk: %w", err)
|
||||
}
|
||||
|
||||
var m nodev1.GuardianKey
|
||||
err = prototext.Unmarshal(b, &m)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize private key from disk: %w", err)
|
||||
}
|
||||
|
||||
gk, err := ethcrypto.ToECDSA(m.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize key data: %w", err)
|
||||
}
|
||||
|
||||
return gk, nil
|
||||
}
|
||||
|
||||
// writeGuardianKey serializes a guardian key and writes it to disk.
|
||||
func writeGuardianKey(key *ecdsa.PrivateKey, description string, filename string) error {
|
||||
m := &nodev1.GuardianKey{
|
||||
Description: description,
|
||||
Data: ethcrypto.FromECDSA(key),
|
||||
Pubkey: ethcrypto.PubkeyToAddress(key.PublicKey).String(),
|
||||
}
|
||||
|
||||
b, err := prototext.MarshalOptions{Multiline: true, EmitASCII: true}.Marshal(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filename, b, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateDevnetGuardianKey returns a deterministic testnet key.
|
||||
func generateDevnetGuardianKey() (*ecdsa.PrivateKey, error) {
|
||||
// Figure out our devnet index
|
||||
idx, err := devnet.GetDevnetIndex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate guardian key
|
||||
return devnet.DeterministicEcdsaKeyByIndex(ethcrypto.S256(), uint64(idx)), nil
|
||||
}
|
|
@ -1,40 +1,14 @@
|
|||
package guardiand
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
p2pcrypto "github.com/libp2p/go-libp2p-core/crypto"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/certusone/wormhole/bridge/pkg/devnet"
|
||||
)
|
||||
|
||||
func loadGuardianKey(logger *zap.Logger) *ecdsa.PrivateKey {
|
||||
var gk *ecdsa.PrivateKey
|
||||
|
||||
if *unsafeDevMode {
|
||||
// Figure out our devnet index
|
||||
idx, err := devnet.GetDevnetIndex()
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to parse hostname - are we running in devnet?")
|
||||
}
|
||||
|
||||
// Generate guardian key
|
||||
gk = devnet.DeterministicEcdsaKeyByIndex(ethcrypto.S256(), uint64(idx))
|
||||
} else {
|
||||
panic("not implemented") // TODO
|
||||
}
|
||||
|
||||
logger.Info("Loaded guardian key", zap.String(
|
||||
"address", ethcrypto.PubkeyToAddress(gk.PublicKey).String()))
|
||||
|
||||
return gk
|
||||
}
|
||||
|
||||
func getOrCreateNodeKey(logger *zap.Logger, path string) (p2pcrypto.PrivKey, error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
|
@ -76,6 +76,8 @@ spec:
|
|||
- --ethConfirmations
|
||||
- '3'
|
||||
- --unsafeDevMode
|
||||
- --bridgeKey
|
||||
- /tmp/bridge.key
|
||||
# - --logLevel=debug
|
||||
securityContext:
|
||||
capabilities:
|
||||
|
|
|
@ -6,4 +6,16 @@ option go_package = "proto/node/v1;nodev1";
|
|||
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
service Node {}
|
||||
service Node {
|
||||
}
|
||||
|
||||
message GuardianKey {
|
||||
// description is an optional, free-form description text set by the operator.
|
||||
string description = 1;
|
||||
|
||||
// data is the binary representation of the secp256k1 private key.
|
||||
bytes data = 2;
|
||||
|
||||
// pubkey is a human-readable representation of the key, included for operator convenience.
|
||||
string pubkey = 3;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue