node: add token bridge governance VAA support
Change-Id: I731161f03590ce73145a1686eb2e62cfe19c8223
This commit is contained in:
parent
7998d04554
commit
2022b55fd4
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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},
|
||||
}
|
||||
|
|
|
@ -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).
|
||||
|
|
Loading…
Reference in New Issue