From 6fd6cb9f02c32344e2cc16be735612dfc6ed0f30 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 8 Oct 2021 19:41:43 +0200 Subject: [PATCH] node: add BridgeUpgradeContract governance VAA Example VAA produced by the template: (*vaa.VAA)(0xc0004f4510)({ Version: (uint8) 1, GuardianSetIndex: (uint32) 0, Signatures: ([]*vaa.Signature) (len=1 cap=1) { (*vaa.Signature)(0xc0003b0370)({ Index: (uint8) 0, Signature: (vaa.SignatureData) (len=65 cap=65) 0f97ec9093c21ccc4ce544898ed5c21b66ab4c90be894642fbb43474ed9fb48a26d6e12f3397b9fdab160fee64e797d26599a2a9d81a4bf4bc98970b5fa5122501 }) }, Timestamp: (time.Time) 1970-01-01 00:00:00 +0000 UTC, Nonce: (uint32) 1375049878, Sequence: (uint64) 3557202656914991802, ConsistencyLevel: (uint8) 32, EmitterChain: (vaa.ChainID) solana, EmitterAddress: (vaa.Address) (len=32 cap=32) 0000000000000000000000000000000000000000000000000000000000000004, Payload: ([]uint8) (len=67 cap=1000) { 00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000010 00 00 00 00 00 54 6f 6b 65 6e 42 72 69 64 67 65 |.....TokenBridge| 00000020 02 00 05 00 00 00 00 00 00 00 00 00 00 00 00 02 |................| 00000030 90 fb 16 72 08 af 45 5b b1 37 78 01 63 b7 b7 a9 |...r..E[.7x.c...| 00000040 a1 0c 16 |...| } }) Change-Id: Ibe95db01e1bc0a9c36e1be06920a389db886fdd1 --- node/cmd/guardiand/adminserver.go | 31 +++++++++++++++++++++++++ node/cmd/guardiand/admintemplate.go | 35 +++++++++++++++++++++++++++++ node/pkg/vaa/payloads.go | 29 ++++++++++++++++++++++++ proto/node/v1/node.proto | 12 ++++++++++ 4 files changed, 107 insertions(+) diff --git a/node/cmd/guardiand/adminserver.go b/node/cmd/guardiand/adminserver.go index 80b26e673..f40426d38 100644 --- a/node/cmd/guardiand/adminserver.go +++ b/node/cmd/guardiand/adminserver.go @@ -122,6 +122,35 @@ func tokenBridgeRegisterChain(req *nodev1.BridgeRegisterChain, guardianSetIndex return v, nil } +// tokenBridgeUpgradeContract converts a nodev1.TokenBridgeRegisterChain message to its canonical VAA representation. +// Returns an error if the data is invalid. +func tokenBridgeUpgradeContract(req *nodev1.BridgeUpgradeContract, guardianSetIndex uint32, nonce uint32, sequence uint64) (*vaa.VAA, error) { + if req.TargetChainId > math.MaxUint16 { + return nil, errors.New("invalid target_chain_id") + } + + b, err := hex.DecodeString(req.NewContract) + if err != nil { + return nil, errors.New("invalid new contract address (expected hex)") + } + + if len(b) != 32 { + return nil, errors.New("invalid new contract address (expected 32 bytes)") + } + + newContract := vaa.Address{} + copy(newContract[:], b) + + v := vaa.CreateGovernanceVAA(nonce, sequence, guardianSetIndex, + vaa.BodyTokenBridgeUpgradeContract{ + Module: req.Module, + TargetChainID: vaa.ChainID(req.TargetChainId), + NewContract: newContract, + }.Serialize()) + + return v, nil +} + func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *nodev1.InjectGovernanceVAARequest) (*nodev1.InjectGovernanceVAAResponse, error) { s.logger.Info("governance VAA injected via admin socket", zap.String("request", req.String())) @@ -136,6 +165,8 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, req.CurrentSetIndex, req.Nonce, req.Sequence) case *nodev1.InjectGovernanceVAARequest_BridgeRegisterChain: v, err = tokenBridgeRegisterChain(payload.BridgeRegisterChain, req.CurrentSetIndex, req.Nonce, req.Sequence) + case *nodev1.InjectGovernanceVAARequest_BridgeContractUpgrade: + v, err = tokenBridgeUpgradeContract(payload.BridgeContractUpgrade, req.CurrentSetIndex, req.Nonce, req.Sequence) default: panic(fmt.Sprintf("unsupported VAA type: %T", payload)) } diff --git a/node/cmd/guardiand/admintemplate.go b/node/cmd/guardiand/admintemplate.go index 73135b2b9..dc72cd900 100644 --- a/node/cmd/guardiand/admintemplate.go +++ b/node/cmd/guardiand/admintemplate.go @@ -24,6 +24,7 @@ func init() { TemplateCmd.AddCommand(AdminClientGuardianSetTemplateCmd) TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd) TemplateCmd.AddCommand(AdminClientTokenBridgeRegisterChainCmd) + TemplateCmd.AddCommand(AdminClientTokenBridgeUpgradeContractCmd) } var TemplateCmd = &cobra.Command{ @@ -52,6 +53,13 @@ var AdminClientTokenBridgeRegisterChainCmd = &cobra.Command{ Args: cobra.ExactArgs(1), } +var AdminClientTokenBridgeUpgradeContractCmd = &cobra.Command{ + Use: "token-bridge-upgrade-contract [FILENAME]", + Short: "Generate an empty token bridge contract upgrade template at specified path (offline)", + Run: runTokenBridgeUpgradeContractTemplate, + Args: cobra.ExactArgs(1), +} + func runGuardianSetTemplate(cmd *cobra.Command, args []string) { path := args[0] @@ -136,3 +144,30 @@ func runTokenBridgeRegisterChainTemplate(cmd *cobra.Command, args []string) { log.Fatal(err) } } + +func runTokenBridgeUpgradeContractTemplate(cmd *cobra.Command, args []string) { + path := args[0] + + m := &nodev1.InjectGovernanceVAARequest{ + CurrentSetIndex: uint32(*templateGuardianIndex), + Sequence: rand.Uint64(), + Nonce: rand.Uint32(), + Payload: &nodev1.InjectGovernanceVAARequest_BridgeContractUpgrade{ + BridgeContractUpgrade: &nodev1.BridgeUpgradeContract{ + Module: "TokenBridge", + TargetChainId: 5, + NewContract: "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) + } +} diff --git a/node/pkg/vaa/payloads.go b/node/pkg/vaa/payloads.go index acec62ea5..fcd56394b 100644 --- a/node/pkg/vaa/payloads.go +++ b/node/pkg/vaa/payloads.go @@ -28,6 +28,13 @@ type ( ChainID ChainID EmitterAddress Address } + + // BodyTokenBridgeUpgradeContract is a governance message to upgrade the token bridge. + BodyTokenBridgeUpgradeContract struct { + Module string + TargetChainID ChainID + NewContract Address + } ) func (b BodyContractUpgrade) Serialize() []byte { @@ -87,3 +94,25 @@ func (r BodyTokenBridgeRegisterChain) Serialize() []byte { return buf.Bytes() } + +func (r BodyTokenBridgeUpgradeContract) Serialize() []byte { + if len(r.Module) > 32 { + panic("module longer than 32 byte") + } + + buf := &bytes.Buffer{} + + // Write token bridge header + for i := 0; i < (32 - len(r.Module)); i++ { + buf.WriteByte(0x00) + } + buf.Write([]byte(r.Module)) + // Write action ID + MustWrite(buf, binary.BigEndian, uint8(2)) + // Write target chain + MustWrite(buf, binary.BigEndian, r.TargetChainID) + // Write emitter address of chain to be registered + buf.Write(r.NewContract[:]) + + return buf.Bytes() +} diff --git a/proto/node/v1/node.proto b/proto/node/v1/node.proto index 0bf05ce78..5bd17f57e 100644 --- a/proto/node/v1/node.proto +++ b/proto/node/v1/node.proto @@ -46,6 +46,7 @@ message InjectGovernanceVAARequest { // Token bridge and NFT module BridgeRegisterChain bridge_register_chain = 12; + BridgeUpgradeContract bridge_contract_upgrade = 13; } } @@ -97,6 +98,17 @@ message ContractUpgrade { bytes new_contract = 2; } +message BridgeUpgradeContract { + // Module identifier of the token or NFT bridge (typically "TokenBridge" or "NFTBridge"). + string module = 1; + + // ID of the chain where the bridge contract should be updated (uint16). + uint32 target_chain_id = 2; + + // Address of the new program/contract. + string new_contract = 3; +} + message FindMissingMessagesRequest { // Emitter chain ID to iterate. uint32 emitter_chain = 1;