Explorar o código

node: add token bridge governance VAA support

Change-Id: I731161f03590ce73145a1686eb2e62cfe19c8223
Leo %!s(int64=4) %!d(string=hai) anos
pai
achega
2022b55fd4

+ 44 - 23
node/cmd/guardiand/adminserver.go

@@ -2,25 +2,24 @@ package guardiand
 
 import (
 	"context"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"github.com/certusone/wormhole/node/pkg/db"
 	publicrpcv1 "github.com/certusone/wormhole/node/pkg/proto/publicrpc/v1"
 	"github.com/certusone/wormhole/node/pkg/publicrpc"
+	ethcommon "github.com/ethereum/go-ethereum/common"
 	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
 	grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"
 	grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags"
 	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
-	"math"
-	"net"
-	"os"
-	"time"
-
-	ethcommon "github.com/ethereum/go-ethereum/common"
 	"go.uber.org/zap"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	"math"
+	"net"
+	"os"
 
 	"github.com/certusone/wormhole/node/pkg/common"
 	nodev1 "github.com/certusone/wormhole/node/pkg/proto/node/v1"
@@ -36,7 +35,7 @@ type nodePrivilegedService struct {
 
 // adminGuardianSetUpdateToVAA converts a nodev1.GuardianSetUpdate message to its canonical VAA representation.
 // Returns an error if the data is invalid.
-func adminGuardianSetUpdateToVAA(req *nodev1.GuardianSetUpdate, guardianSetIndex uint32, timestamp uint32) (*vaa.VAA, error) {
+func adminGuardianSetUpdateToVAA(req *nodev1.GuardianSetUpdate, guardianSetIndex uint32, nonce uint32, sequence uint64) (*vaa.VAA, error) {
 	if len(req.Guardians) == 0 {
 		return nil, errors.New("empty guardian set specified")
 	}
@@ -61,22 +60,18 @@ func adminGuardianSetUpdateToVAA(req *nodev1.GuardianSetUpdate, guardianSetIndex
 		addrs[i] = ethAddr
 	}
 
-	v := &vaa.VAA{
-		Version:          vaa.SupportedVAAVersion,
-		GuardianSetIndex: guardianSetIndex,
-		Timestamp:        time.Unix(int64(timestamp), 0),
-		Payload: vaa.BodyGuardianSetUpdate{
+	v := vaa.CreateGovernanceVAA(nonce, sequence, guardianSetIndex,
+		vaa.BodyGuardianSetUpdate{
 			Keys:     addrs,
 			NewIndex: guardianSetIndex + 1,
-		}.Serialize(),
-	}
+		}.Serialize())
 
 	return v, nil
 }
 
 // adminContractUpgradeToVAA converts a nodev1.ContractUpgrade message to its canonical VAA representation.
 // Returns an error if the data is invalid.
-func adminContractUpgradeToVAA(req *nodev1.ContractUpgrade, guardianSetIndex uint32, timestamp uint32) (*vaa.VAA, error) {
+func adminContractUpgradeToVAA(req *nodev1.ContractUpgrade, guardianSetIndex uint32, nonce uint32, sequence uint64) (*vaa.VAA, error) {
 	if len(req.NewContract) != 32 {
 		return nil, errors.New("invalid new_contract address")
 	}
@@ -88,16 +83,40 @@ func adminContractUpgradeToVAA(req *nodev1.ContractUpgrade, guardianSetIndex uin
 	newContractAddress := vaa.Address{}
 	copy(newContractAddress[:], req.NewContract)
 
-	v := &vaa.VAA{
-		Version:          vaa.SupportedVAAVersion,
-		GuardianSetIndex: guardianSetIndex,
-		Timestamp:        time.Unix(int64(timestamp), 0),
-		Payload: vaa.BodyContractUpgrade{
+	v := vaa.CreateGovernanceVAA(nonce, sequence, guardianSetIndex,
+		vaa.BodyContractUpgrade{
 			ChainID:     vaa.ChainID(req.ChainId),
 			NewContract: newContractAddress,
-		}.Serialize(),
+		}.Serialize())
+
+	return v, nil
+}
+
+// tokenBridgeRegisterChain converts a nodev1.TokenBridgeRegisterChain message to its canonical VAA representation.
+// Returns an error if the data is invalid.
+func tokenBridgeRegisterChain(req *nodev1.TokenBridgeRegisterChain, guardianSetIndex uint32, nonce uint32, sequence uint64) (*vaa.VAA, error) {
+	if req.ChainId > math.MaxUint8 {
+		return nil, errors.New("invalid chain_id")
+	}
+
+	b, err := hex.DecodeString(req.EmitterAddress)
+	if err != nil {
+		return nil, errors.New("invalid emitter address encoding (expected hex)")
+	}
+
+	if len(b) != 32 {
+		return nil, errors.New("invalid emitter address (expected 32 bytes)")
 	}
 
+	emitterAddress := vaa.Address{}
+	copy(emitterAddress[:], req.EmitterAddress)
+
+	v := vaa.CreateGovernanceVAA(nonce, sequence, guardianSetIndex,
+		vaa.BodyTokenBridgeRegisterChain{
+			ChainID:        vaa.ChainID(req.ChainId),
+			EmitterAddress: emitterAddress,
+		}.Serialize())
+
 	return v, nil
 }
 
@@ -110,9 +129,11 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no
 	)
 	switch payload := req.Payload.(type) {
 	case *nodev1.InjectGovernanceVAARequest_GuardianSet:
-		v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, req.CurrentSetIndex, req.Timestamp)
+		v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, req.CurrentSetIndex, req.Nonce, req.Sequence)
 	case *nodev1.InjectGovernanceVAARequest_ContractUpgrade:
-		v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, req.CurrentSetIndex, req.Timestamp)
+		v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, req.CurrentSetIndex, req.Nonce, req.Sequence)
+	case *nodev1.InjectGovernanceVAARequest_TokenBridgeRegisterChain:
+		v, err = tokenBridgeRegisterChain(payload.TokenBridgeRegisterChain, req.CurrentSetIndex, req.Nonce, req.Sequence)
 	default:
 		panic(fmt.Sprintf("unsupported VAA type: %T", payload))
 	}

+ 38 - 6
node/cmd/guardiand/admintemplate.go

@@ -2,6 +2,7 @@ package guardiand
 
 import (
 	"fmt"
+	"github.com/tendermint/tendermint/libs/rand"
 	"io/ioutil"
 	"log"
 
@@ -22,6 +23,7 @@ func init() {
 
 	TemplateCmd.AddCommand(AdminClientGuardianSetTemplateCmd)
 	TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd)
+	TemplateCmd.AddCommand(AdminClientTokenBridgeRegisterChainCmd)
 }
 
 var TemplateCmd = &cobra.Command{
@@ -43,6 +45,13 @@ var AdminClientContractUpgradeTemplateCmd = &cobra.Command{
 	Args:  cobra.ExactArgs(1),
 }
 
+var AdminClientTokenBridgeRegisterChainCmd = &cobra.Command{
+	Use:   "token-bridge-register-chain [FILENAME]",
+	Short: "Generate an empty token bridge chain registration template at specified path (offline)",
+	Run:   runTokenBridgeRegisterChainTemplate,
+	Args:  cobra.ExactArgs(1),
+}
+
 func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
 	path := args[0]
 
@@ -58,9 +67,8 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
 
 	m := &nodev1.InjectGovernanceVAARequest{
 		CurrentSetIndex: uint32(*templateGuardianIndex),
-		// Timestamp is hardcoded to make it reproducible on different devnet nodes.
-		// In production, a real UNIX timestamp should be used (see node.proto).
-		Timestamp: 1605744545,
+		Sequence: 1234,
+		Nonce: rand.Uint32(),
 		Payload: &nodev1.InjectGovernanceVAARequest_GuardianSet{
 			GuardianSet: &nodev1.GuardianSetUpdate{Guardians: guardians},
 		},
@@ -82,9 +90,8 @@ func runContractUpgradeTemplate(cmd *cobra.Command, args []string) {
 
 	m := &nodev1.InjectGovernanceVAARequest{
 		CurrentSetIndex: uint32(*templateGuardianIndex),
-		// Timestamp is hardcoded to make it reproducible on different devnet nodes.
-		// In production, a real UNIX timestamp should be used (see node.proto).
-		Timestamp: 1605744545,
+		Sequence: 1234,
+		Nonce: rand.Uint32(),
 		Payload: &nodev1.InjectGovernanceVAARequest_ContractUpgrade{
 			ContractUpgrade: &nodev1.ContractUpgrade{
 				ChainId:     1,
@@ -103,3 +110,28 @@ func runContractUpgradeTemplate(cmd *cobra.Command, args []string) {
 		log.Fatal(err)
 	}
 }
+func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) {
+	path := args[0]
+
+	m := &nodev1.InjectGovernanceVAARequest{
+		CurrentSetIndex: uint32(*templateGuardianIndex),
+		Sequence: rand.Uint64(),
+		Nonce: rand.Uint32(),
+		Payload: &nodev1.InjectGovernanceVAARequest_TokenBridgeRegisterChain{
+			TokenBridgeRegisterChain: &nodev1.TokenBridgeRegisterChain{
+				ChainId:        5,
+				EmitterAddress: "0000000000000000000000000290FB167208Af455bB137780163b7B7a9a10C16",
+			},
+		},
+	}
+
+	b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
+	if err != nil {
+		panic(err)
+	}
+
+	err = ioutil.WriteFile(path, b, 0640)
+	if err != nil {
+		log.Fatal(err)
+	}
+}

+ 7 - 2
node/cmd/guardiand/adminverify.go

@@ -1,6 +1,7 @@
 package guardiand
 
 import (
+	"fmt"
 	"github.com/certusone/wormhole/node/pkg/vaa"
 	"io/ioutil"
 	"log"
@@ -38,9 +39,13 @@ func runGovernanceVAAVerify(cmd *cobra.Command, args []string) {
 	)
 	switch payload := msg.Payload.(type) {
 	case *nodev1.InjectGovernanceVAARequest_GuardianSet:
-		v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, msg.CurrentSetIndex, msg.Timestamp)
+		v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, msg.CurrentSetIndex, msg.Nonce, msg.Sequence)
 	case *nodev1.InjectGovernanceVAARequest_ContractUpgrade:
-		v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, msg.CurrentSetIndex, msg.Timestamp)
+		v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, msg.CurrentSetIndex, msg.Nonce, msg.Sequence)
+	case *nodev1.InjectGovernanceVAARequest_TokenBridgeRegisterChain:
+		v, err = tokenBridgeRegisterChain(payload.TokenBridgeRegisterChain, msg.CurrentSetIndex, msg.Nonce, msg.Sequence)
+	default:
+		panic(fmt.Sprintf("unsupported VAA type: %T", payload))
 	}
 	if err != nil {
 		log.Fatalf("invalid update: %v", err)

+ 1 - 1
node/pkg/vaa/governance.go

@@ -9,7 +9,7 @@ var governanceChain = ChainIDSolana
 
 func CreateGovernanceVAA(nonce uint32, sequence uint64, guardianSetIndex uint32, payload []byte) *VAA {
 	vaa := &VAA{
-		Version:          1,
+		Version:          SupportedVAAVersion,
 		GuardianSetIndex: guardianSetIndex,
 		Signatures:       nil,
 		Timestamp:        time.Unix(0, 0),

+ 7 - 5
node/pkg/vaa/payloads.go

@@ -9,6 +9,9 @@ import (
 // CoreModule is the identifier of the Core module (which is used for governance messages)
 var CoreModule = []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0x43, 0x6f, 0x72, 0x65}
 
+// TokenBridgeModule is the identifier of the token bridge module ("TokenBridge")
+var TokenBridgeModule = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x72, 0x69, 0x64, 0x67, 0x65}
+
 type (
 	// BodyContractUpgrade is a governance message to perform a contract upgrade of the core module
 	BodyContractUpgrade struct {
@@ -22,9 +25,8 @@ type (
 		NewIndex uint32
 	}
 
-	// BodyRegisterChain is a governance message to register a chain on the token bridge
-	BodyRegisterChain struct {
-		Header         [32]byte
+	// BodyTokenBridgeRegisterChain is a governance message to register a chain on the token bridge
+	BodyTokenBridgeRegisterChain struct {
 		ChainID        ChainID
 		EmitterAddress Address
 	}
@@ -64,11 +66,11 @@ func (b BodyGuardianSetUpdate) Serialize() []byte {
 	return buf.Bytes()
 }
 
-func (r BodyRegisterChain) Serialize() []byte {
+func (r BodyTokenBridgeRegisterChain) Serialize() []byte {
 	buf := &bytes.Buffer{}
 
 	// Write token bridge header
-	buf.Write(r.Header[:])
+	buf.Write(TokenBridgeModule)
 	// Write action ID
 	MustWrite(buf, binary.BigEndian, uint8(1))
 	// Write target chain (0 = universal)

+ 1 - 2
node/pkg/vaa/types_test.go

@@ -92,8 +92,7 @@ func TestBodyRegisterChain_Serialize(t *testing.T) {
 
 	var headerB [32]byte
 	copy(headerB[:], header)
-	msg := &BodyRegisterChain{
-		Header:         headerB,
+	msg := &BodyTokenBridgeRegisterChain{
 		ChainID:        8,
 		EmitterAddress: Address{1, 2, 3, 4},
 	}

+ 18 - 13
proto/node/v1/node.proto

@@ -20,23 +20,20 @@ message InjectGovernanceVAARequest {
   // Index of the current guardian set.
   uint32 current_set_index = 1;
 
-  // UNIX timestamp (s) of the VAA to be created. The timestamp is informational and will be part
-  // of the VAA submitted to the chain. It's part of the VAA digest and has to be identical across nodes and
-  // is critical for replay protection - a given event may only ever be observed with the same timestamp,
-  // otherwise, it may be possible to execute it multiple times.
+  // Sequence number. This is critical for replay protection - make sure the sequence number
+  // is unique for every new manually injected governance VAA. Sequences are tracked
+  // by emitter, and manually injected VAAs all use a single hardcoded emitter.
   //
-  // For messages, the timestamp identifies the block that the message belongs to.
+  // We use random sequence numbers for the manual emitter.
+  uint64 sequence = 2;
 
-  // For governance VAAs, guardians inject the VAA manually. Best practice is to pick a timestamp which roughly matches
-  // the timing of the off-chain ceremony used to achieve consensus. For guardian set updates, the actual on-chain
-  // guardian set creation timestamp will be set when the VAA is accepted on each chain.
-  //
-  // This is a uint32 to match the on-chain timestamp representation. This becomes a problem in 2106 (sorry).
-  uint32 timestamp = 2;
+  // Random nonce for disambiguation. Must be identical across all nodes.
+  uint32 nonce = 3;
 
   oneof payload{
-    GuardianSetUpdate guardian_set = 3;
-    ContractUpgrade contract_upgrade = 4;
+    GuardianSetUpdate guardian_set = 10;
+    ContractUpgrade contract_upgrade = 11;
+    TokenBridgeRegisterChain token_bridge_register_chain = 12;
   }
 }
 
@@ -68,6 +65,14 @@ message GuardianKey {
   bool unsafe_deterministic_key = 2;
 }
 
+message TokenBridgeRegisterChain {
+  // ID of the chain to be registered.
+  uint32 chain_id = 1;
+
+  // Hex-encoded emitter address to be registered (without leading 0x).
+  string emitter_address = 2;
+}
+
 // ContractUpgrade represents a Wormhole contract update to be submitted to and signed by the node.
 message ContractUpgrade {
   // ID of the chain where the Wormhole contract should be updated (uint8).