From efa03ef73c19e9882caa31fb7aa5872d7b12094e Mon Sep 17 00:00:00 2001 From: Hendrik Hofstadt Date: Tue, 19 Jan 2021 13:01:45 +0100 Subject: [PATCH] Implement upgradeability (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implement contract upgrade VAA action * naming and (╯°□°)╯︵ ┻━┻ * Carefully unflip table and replace broken cutlery ┬─┬ノ( ◕◡◕ ノ) * fix and automate upgradeability * document contract upgrade call * Update comments * Exhaustiveness check in VAA payload switch * Fix typo Co-authored-by: Leo --- bridge/cmd/guardiand/adminclient.go | 17 +- bridge/cmd/guardiand/adminserver.go | 57 +++++- bridge/cmd/guardiand/admintemplate.go | 60 +++++- bridge/cmd/guardiand/adminverify.go | 23 ++- bridge/cmd/root.go | 1 + bridge/pkg/processor/observation.go | 11 ++ bridge/pkg/vaa/structs.go | 37 ++++ bridge/pkg/vaa/types_test.go | 18 ++ docs/protocol.md | 14 ++ docs/solana_program.md | 8 + proto/node/v1/node.proto | 100 +++++----- scripts/test-injection.sh | 6 +- solana/Cargo.lock | 257 ++++++++++++++++---------- solana/agent/Cargo.toml | 10 +- solana/bridge/Cargo.toml | 4 +- solana/bridge/src/error.rs | 3 + solana/bridge/src/error_program.rs | 1 + solana/bridge/src/instruction.rs | 6 + solana/bridge/src/processor.rs | 33 ++++ solana/bridge/src/vaa.rs | 90 +++++++++ solana/cli/Cargo.toml | 16 +- third_party/solana/Dockerfile | 2 +- 22 files changed, 580 insertions(+), 194 deletions(-) diff --git a/bridge/cmd/guardiand/adminclient.go b/bridge/cmd/guardiand/adminclient.go index 828cd84d..1e9674ca 100644 --- a/bridge/cmd/guardiand/adminclient.go +++ b/bridge/cmd/guardiand/adminclient.go @@ -26,8 +26,7 @@ func init() { } AdminCmd.AddCommand(AdminClientInjectGuardianSetUpdateCmd) - AdminCmd.AddCommand(AdminClientGuardianSetTemplateCmd) - AdminCmd.AddCommand(AdminClientGuardianSetVerifyCmd) + AdminCmd.AddCommand(AdminClientGovernanceVAAVerifyCmd) } var AdminCmd = &cobra.Command{ @@ -36,9 +35,9 @@ var AdminCmd = &cobra.Command{ } var AdminClientInjectGuardianSetUpdateCmd = &cobra.Command{ - Use: "guardian-set-update-inject", - Short: "Inject and sign a guardian set update from a prototxt file (see docs!)", - Run: runInjectGuardianSetUpdate, + Use: "governance-vaa-inject", + Short: "Inject and sign a governance VAA from a prototxt file (see docs!)", + Run: runInjectGovernanceVAA, Args: cobra.ExactArgs(1), } @@ -53,7 +52,7 @@ func getAdminClient(ctx context.Context, addr string) (*grpc.ClientConn, error, return conn, err, c } -func runInjectGuardianSetUpdate(cmd *cobra.Command, args []string) { +func runInjectGovernanceVAA(cmd *cobra.Command, args []string) { path := args[0] ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -66,15 +65,15 @@ func runInjectGuardianSetUpdate(cmd *cobra.Command, args []string) { log.Fatalf("failed to read file: %v", err) } - var msg nodev1.GuardianSetUpdate + var msg nodev1.InjectGovernanceVAARequest err = prototext.Unmarshal(b, &msg) if err != nil { log.Fatalf("failed to deserialize: %v", err) } - resp, err := c.SubmitGuardianSetVAA(ctx, &nodev1.SubmitGuardianSetVAARequest{GuardianSet: &msg}) + resp, err := c.InjectGovernanceVAA(ctx, &msg) if err != nil { - log.Fatalf("failed to submit guardian set update: %v", err) + log.Fatalf("failed to submit governance VAA: %v", err) } log.Printf("VAA successfully injected with digest %s", hexutils.BytesToHex(resp.Digest)) diff --git a/bridge/cmd/guardiand/adminserver.go b/bridge/cmd/guardiand/adminserver.go index 4979e5db..b0c8035c 100644 --- a/bridge/cmd/guardiand/adminserver.go +++ b/bridge/cmd/guardiand/adminserver.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "math" "net" "os" "time" @@ -28,7 +29,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) (*vaa.VAA, error) { +func adminGuardianSetUpdateToVAA(req *nodev1.GuardianSetUpdate, guardianSetIndex uint32, timestamp uint32) (*vaa.VAA, error) { if len(req.Guardians) == 0 { return nil, errors.New("empty guardian set specified") } @@ -48,21 +49,59 @@ func adminGuardianSetUpdateToVAA(req *nodev1.GuardianSetUpdate) (*vaa.VAA, error v := &vaa.VAA{ Version: vaa.SupportedVAAVersion, - GuardianSetIndex: req.CurrentSetIndex, - Timestamp: time.Unix(int64(req.Timestamp), 0), + GuardianSetIndex: guardianSetIndex, + Timestamp: time.Unix(int64(timestamp), 0), Payload: &vaa.BodyGuardianSetUpdate{ Keys: addrs, - NewIndex: req.CurrentSetIndex + 1, + NewIndex: guardianSetIndex + 1, }, } return v, nil } -func (s *nodePrivilegedService) SubmitGuardianSetVAA(ctx context.Context, req *nodev1.SubmitGuardianSetVAARequest) (*nodev1.SubmitGuardianSetVAAResponse, error) { - s.logger.Info("guardian set injected via admin socket", zap.String("request", req.String())) +// 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) { + if len(req.NewContract) != 32 { + return nil, errors.New("invalid new_contract address") + } - v, err := adminGuardianSetUpdateToVAA(req.GuardianSet) + if req.ChainId > math.MaxUint8 { + return nil, errors.New("invalid chain_id") + } + + newContractAddress := vaa.Address{} + copy(newContractAddress[:], req.NewContract) + + v := &vaa.VAA{ + Version: vaa.SupportedVAAVersion, + GuardianSetIndex: guardianSetIndex, + Timestamp: time.Unix(int64(timestamp), 0), + Payload: &vaa.BodyContractUpgrade{ + ChainID: uint8(req.ChainId), + NewContract: newContractAddress, + }, + } + + 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())) + + var ( + v *vaa.VAA + err error + ) + switch payload := req.Payload.(type) { + case *nodev1.InjectGovernanceVAARequest_GuardianSet: + v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, req.CurrentSetIndex, req.Timestamp) + case *nodev1.InjectGovernanceVAARequest_ContractUpgrade: + v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, req.CurrentSetIndex, req.Timestamp) + default: + panic(fmt.Sprintf("unsupported VAA type: %T", payload)) + } if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } @@ -73,14 +112,14 @@ func (s *nodePrivilegedService) SubmitGuardianSetVAA(ctx context.Context, req *n panic(err) } - s.logger.Info("guardian set VAA constructed", + s.logger.Info("governance VAA constructed", zap.Any("vaa", v), zap.String("digest", digest.String()), ) s.injectC <- v - return &nodev1.SubmitGuardianSetVAAResponse{Digest: digest.Bytes()}, nil + return &nodev1.InjectGovernanceVAAResponse{Digest: digest.Bytes()}, nil } func adminServiceRunnable(logger *zap.Logger, socketPath string, injectC chan<- *vaa.VAA) (supervisor.Runnable, error) { diff --git a/bridge/cmd/guardiand/admintemplate.go b/bridge/cmd/guardiand/admintemplate.go index b6fdbce8..47189c0d 100644 --- a/bridge/cmd/guardiand/admintemplate.go +++ b/bridge/cmd/guardiand/admintemplate.go @@ -13,27 +13,42 @@ import ( nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1" ) -var templateNumGuardians *int +var setUpdateNumGuardians *int var templateGuardianIndex *int func init() { - templateNumGuardians = AdminClientGuardianSetTemplateCmd.Flags().Int("num", 1, "Number of devnet guardians in example file") - templateGuardianIndex = AdminClientGuardianSetTemplateCmd.Flags().Int("idx", 0, "Default current guardian set index") + templateGuardianIndex = TemplateCmd.PersistentFlags().Int("idx", 0, "Default current guardian set index") + setUpdateNumGuardians = AdminClientGuardianSetTemplateCmd.Flags().Int("num", 1, "Number of devnet guardians in example file") + + TemplateCmd.AddCommand(AdminClientGuardianSetTemplateCmd) + TemplateCmd.AddCommand(AdminClientContractUpgradeTemplateCmd) +} + +var TemplateCmd = &cobra.Command{ + Use: "template", + Short: "Guardian governance VAA template commands ", } var AdminClientGuardianSetTemplateCmd = &cobra.Command{ - Use: "guardian-set-update-template", + Use: "guardian-set-update", Short: "Generate an empty guardian set template at specified path (offline)", Run: runGuardianSetTemplate, Args: cobra.ExactArgs(1), } +var AdminClientContractUpgradeTemplateCmd = &cobra.Command{ + Use: "contract-upgrade", + Short: "Generate an empty contract upgrade template at specified path (offline)", + Run: runContractUpgradeTemplate, + Args: cobra.ExactArgs(1), +} + func runGuardianSetTemplate(cmd *cobra.Command, args []string) { path := args[0] // Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture. - guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *templateNumGuardians) - for i := 0; i < *templateNumGuardians; i++ { + guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians) + for i := 0; i < *setUpdateNumGuardians; i++ { k := devnet.DeterministicEcdsaKeyByIndex(crypto.S256(), uint64(i)) guardians[i] = &nodev1.GuardianSetUpdate_Guardian{ Pubkey: crypto.PubkeyToAddress(k.PublicKey).Hex(), @@ -41,12 +56,41 @@ func runGuardianSetTemplate(cmd *cobra.Command, args []string) { } } - m := &nodev1.GuardianSetUpdate{ + 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, - Guardians: guardians, + Payload: &nodev1.InjectGovernanceVAARequest_GuardianSet{ + GuardianSet: &nodev1.GuardianSetUpdate{Guardians: guardians}, + }, + } + + 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) + } +} + +func runContractUpgradeTemplate(cmd *cobra.Command, args []string) { + path := args[0] + + 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, + Payload: &nodev1.InjectGovernanceVAARequest_ContractUpgrade{ + ContractUpgrade: &nodev1.ContractUpgrade{ + ChainId: 1, + NewContract: make([]byte, 32), + }, + }, } b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m) diff --git a/bridge/cmd/guardiand/adminverify.go b/bridge/cmd/guardiand/adminverify.go index 7e4c4134..5f136589 100644 --- a/bridge/cmd/guardiand/adminverify.go +++ b/bridge/cmd/guardiand/adminverify.go @@ -1,6 +1,7 @@ package guardiand import ( + "github.com/certusone/wormhole/bridge/pkg/vaa" "io/ioutil" "log" @@ -11,14 +12,14 @@ import ( nodev1 "github.com/certusone/wormhole/bridge/pkg/proto/node/v1" ) -var AdminClientGuardianSetVerifyCmd = &cobra.Command{ - Use: "guardian-set-update-verify", - Short: "Verify guardian set update in prototxt format (offline)", - Run: runGuardianSetVerify, +var AdminClientGovernanceVAAVerifyCmd = &cobra.Command{ + Use: "governance-vaa-verify", + Short: "Verify governance vaa in prototxt format (offline)", + Run: runGovernanceVAAVerify, Args: cobra.ExactArgs(1), } -func runGuardianSetVerify(cmd *cobra.Command, args []string) { +func runGovernanceVAAVerify(cmd *cobra.Command, args []string) { path := args[0] b, err := ioutil.ReadFile(path) @@ -26,13 +27,21 @@ func runGuardianSetVerify(cmd *cobra.Command, args []string) { log.Fatalf("failed to read file: %v", err) } - var msg nodev1.GuardianSetUpdate + var msg nodev1.InjectGovernanceVAARequest err = prototext.Unmarshal(b, &msg) if err != nil { log.Fatalf("failed to deserialize: %v", err) } - v, err := adminGuardianSetUpdateToVAA(&msg) + var ( + v *vaa.VAA + ) + switch payload := msg.Payload.(type) { + case *nodev1.InjectGovernanceVAARequest_GuardianSet: + v, err = adminGuardianSetUpdateToVAA(payload.GuardianSet, msg.CurrentSetIndex, msg.Timestamp) + case *nodev1.InjectGovernanceVAARequest_ContractUpgrade: + v, err = adminContractUpgradeToVAA(payload.ContractUpgrade, msg.CurrentSetIndex, msg.Timestamp) + } if err != nil { log.Fatalf("invalid update: %v", err) } diff --git a/bridge/cmd/root.go b/bridge/cmd/root.go index 9181fa47..d1c29d84 100644 --- a/bridge/cmd/root.go +++ b/bridge/cmd/root.go @@ -36,6 +36,7 @@ func init() { rootCmd.AddCommand(guardiand.BridgeCmd) rootCmd.AddCommand(guardiand.KeygenCmd) rootCmd.AddCommand(guardiand.AdminCmd) + rootCmd.AddCommand(guardiand.TemplateCmd) } // initConfig reads in config file and ENV variables if set. diff --git a/bridge/pkg/processor/observation.go b/bridge/pkg/processor/observation.go index 9e2c41b0..eeddef7b 100644 --- a/bridge/pkg/processor/observation.go +++ b/bridge/pkg/processor/observation.go @@ -172,6 +172,17 @@ func (p *Processor) handleObservation(ctx context.Context, m *gossipv1.SignedObs // A guardian set update is broadcast to every chain that we talk to. p.devnetVAASubmission(ctx, signed, hash) p.terraVAASubmission(ctx, signed, hash) + case *vaa.BodyContractUpgrade: + switch t.ChainID { + case vaa.ChainIDSolana: + // Already submitted to Solana. + default: + p.logger.Error("unsupported target chain for contract upgrade", + zap.String("digest", hash), + zap.Any("vaa", signed), + zap.String("bytes", hex.EncodeToString(vaaBytes)), + zap.Uint8("target_chain", t.ChainID)) + } default: panic(fmt.Sprintf("unknown VAA payload type: %+v", v)) } diff --git a/bridge/pkg/vaa/structs.go b/bridge/pkg/vaa/structs.go index 0b2007cd..46697ef1 100644 --- a/bridge/pkg/vaa/structs.go +++ b/bridge/pkg/vaa/structs.go @@ -85,6 +85,13 @@ type ( // NewIndex is the index of the new guardian set NewIndex uint32 } + + BodyContractUpgrade struct { + // ChainID is the chain on which the contract should be upgraded + ChainID uint8 + // NewContract is the address of the account containing the new contract. + NewContract Address + } ) func (a Address) String() string { @@ -106,6 +113,7 @@ func (c ChainID) String() string { const ( ActionGuardianSetUpdate Action = 0x01 + ActionContractUpgrade Action = 0x02 ActionTransfer Action = 0x10 // ChainIDSolana is the ChainID of Solana @@ -182,6 +190,8 @@ func Unmarshal(data []byte) (*VAA, error) { v.Payload, err = parseBodyGuardianSetUpdate(payloadReader) case ActionTransfer: v.Payload, err = parseBodyTransfer(payloadReader) + case ActionContractUpgrade: + v.Payload, err = parseBodyContractUpgrade(payloadReader) default: return nil, fmt.Errorf("unknown action: %d", action) } @@ -403,6 +413,33 @@ func (v *BodyGuardianSetUpdate) serialize() ([]byte, error) { return buf.Bytes(), nil } +func parseBodyContractUpgrade(r io.Reader) (*BodyContractUpgrade, error) { + b := &BodyContractUpgrade{} + + if err := binary.Read(r, binary.BigEndian, &b.ChainID); err != nil { + return nil, fmt.Errorf("failed to read chain id: %w", err) + } + + if n, err := r.Read(b.NewContract[:]); err != nil || n != 32 { + return nil, fmt.Errorf("failed to read new contract address: %w", err) + } + + return b, nil +} + +func (v *BodyContractUpgrade) getActionID() Action { + return ActionContractUpgrade +} + +func (v *BodyContractUpgrade) serialize() ([]byte, error) { + buf := new(bytes.Buffer) + + MustWrite(buf, binary.BigEndian, v.ChainID) + buf.Write(v.NewContract[:]) + + return buf.Bytes(), nil +} + // MustWrite calls binary.Write and panics on errors func MustWrite(w io.Writer, order binary.ByteOrder, data interface{}) { if err := binary.Write(w, order, data); err != nil { diff --git a/bridge/pkg/vaa/types_test.go b/bridge/pkg/vaa/types_test.go index 95269133..4c07b63a 100644 --- a/bridge/pkg/vaa/types_test.go +++ b/bridge/pkg/vaa/types_test.go @@ -61,6 +61,24 @@ func TestSerializeDeserialize(t *testing.T) { }, }, }, + { + name: "ContractUpgrade", + vaa: &VAA{ + Version: 1, + GuardianSetIndex: 9, + Signatures: []*Signature{ + { + Index: 1, + Signature: [65]byte{}, + }, + }, + Timestamp: time.Unix(2837, 0), + Payload: &BodyContractUpgrade{ + ChainID: ChainIDEthereum, + NewContract: Address{1, 3, 4, 5, 2, 3}, + }, + }, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/docs/protocol.md b/docs/protocol.md index e4db570a..0d8ed438 100644 --- a/docs/protocol.md +++ b/docs/protocol.md @@ -146,6 +146,20 @@ uint8 len(keys) The `new_index` must be monotonically increasing and is manually specified here to fix a potential guardian_set index desynchronization between the any of the chains in the system. +##### Contract upgrade + +ID: `0x02` + +Payload: + +``` +uint8 chain_id +[32]uint8 new_contract +``` + +`chain_id` specifies the chain on which the contract should be updated. `new_contract` is the address of the updated +contract. + ##### Transfer ID: `0x10` diff --git a/docs/solana_program.md b/docs/solana_program.md index c7040d02..a1175489 100644 --- a/docs/solana_program.md +++ b/docs/solana_program.md @@ -154,6 +154,14 @@ followed by: | ----- | ------------ | ------------------- | ------ | --------- | ----- | ------- | | 9 | guardian_set_new | GuardianSet | | ✅ | ✅ | ✅ | +##### Contract upgrade + +| Index | Name | Type | signer | writeable | empty | derived | +| ----- | ------------------ | ----------------- | ------ | --------- | ----- | ------- | +| 9 | new_contract | Account | | | ✅ | | +| 10 | program_data | Account | | ✅ | ✅ | ✅ | +| 11 | upgradeable_loader | UpgradeableLoader | | | | | + ##### Transfer: Ethereum (native) -> Solana (wrapped) | Index | Name | Type | signer | writeable | empty | derived | diff --git a/proto/node/v1/node.proto b/proto/node/v1/node.proto index dbbdd6f9..d5a3d808 100644 --- a/proto/node/v1/node.proto +++ b/proto/node/v1/node.proto @@ -9,58 +9,70 @@ import "google/api/annotations.proto"; // NodePrivileged exposes an administrative API. It runs on a UNIX socket and is authenticated // using Linux filesystem permissions. service NodePrivileged { - // SubmitGuardianSetVAA injects a guardian set change VAA into the guardian node. - // The node will inject the VAA into the aggregator and sign/broadcast the VAA signature. - // - // A consensus majority of nodes on the network will have to inject the VAA within the - // VAA timeout window for it to reach consensus. - // - rpc SubmitGuardianSetVAA (SubmitGuardianSetVAARequest) returns (SubmitGuardianSetVAAResponse); + // InjectGovernanceVAA injects a governance VAA into the guardian node. + // The node will inject the VAA into the aggregator and sign/broadcast the VAA signature. + // + // A consensus majority of nodes on the network will have to inject the VAA within the + // VAA timeout window for it to reach consensus. + // + rpc InjectGovernanceVAA (InjectGovernanceVAARequest) returns (InjectGovernanceVAAResponse); +} + +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. + // + // For lockups, the timestamp identifies the block that the lockup belongs to. + + // 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; + + oneof payload{ + GuardianSetUpdate guardian_set = 3; + ContractUpgrade contract_upgrade = 4; + } +} + +message InjectGovernanceVAAResponse { + // Canonical digest of the submitted VAA. + bytes digest = 1; } // GuardianSet represents a new guardian set to be submitted to and signed by the node. // During the genesis procedure, this data structure will be assembled using off-chain collaborative tooling // like GitHub using a human-readable encoding, so readability is a concern. message GuardianSetUpdate { - // Index of the current guardian set to be replaced. - 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. - // - // For lockups, the timestamp identifies the block that the lockup belongs to. For guardian set updates, - // we create the VAA manually. Best practice is to pick a timestamp which roughly matches the expected - // genesis ceremony data. - // - // 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; - - // List of guardian set members. - message Guardian { - // Guardian key pubkey. Stored as hex string with 0x prefix for human readability - - // this is the canonical Ethereum representation. - string pubkey = 1; - // Optional descriptive name. Not stored on any chain, purely informational. - string name = 2; - }; - repeated Guardian guardians = 3; -} - -message SubmitGuardianSetVAARequest { - GuardianSetUpdate guardian_set = 1; -} - -message SubmitGuardianSetVAAResponse { - // Canonical digest of the submitted VAA. - bytes digest = 1; + // List of guardian set members. + message Guardian { + // Guardian key pubkey. Stored as hex string with 0x prefix for human readability - + // this is the canonical Ethereum representation. + string pubkey = 1; + // Optional descriptive name. Not stored on any chain, purely informational. + string name = 2; + }; + repeated Guardian guardians = 3; } // GuardianKey specifies the on-disk format for a node's guardian key. message GuardianKey { - // data is the binary representation of the secp256k1 private key. - bytes data = 1; + // data is the binary representation of the secp256k1 private key. + bytes data = 1; +} + +// 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). + uint32 chain_id = 1; + + // Address of the new program/contract. + bytes new_contract = 2; } diff --git a/scripts/test-injection.sh b/scripts/test-injection.sh index c7c7b488..3fef1735 100755 --- a/scripts/test-injection.sh +++ b/scripts/test-injection.sh @@ -9,10 +9,10 @@ path=/tmp/new-guardianset.prototxt sock=/tmp/admin.sock # Create a no-op update that sets the same 1-node guardian set again. -kubectl exec guardian-${node} -c guardiand -- /guardiand admin guardian-set-update-template --num=1 --idx=${idx} $path +kubectl exec -n wormhole guardian-${node} -c guardiand -- /guardiand template guardian-set-update --num=1 --idx=${idx} $path # Verify and print resulting result. The digest incorporates the current time and is NOT deterministic. -kubectl exec guardian-${node} -c guardiand -- /guardiand admin guardian-set-update-verify $path +kubectl exec -n wormhole guardian-${node} -c guardiand -- /guardiand admin governance-vaa-verify $path # Submit to node -kubectl exec guardian-${node} -c guardiand -- /guardiand admin guardian-set-update-inject --socket $sock $path +kubectl exec -n wormhole guardian-${node} -c guardiand -- /guardiand admin governance-vaa-inject --socket $sock $path diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 4de58d09..76d6081f 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -330,6 +330,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" +[[package]] +name = "bytes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16" + [[package]] name = "bzip2" version = "0.3.3" @@ -823,9 +829,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.7.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", "humantime", @@ -1245,12 +1251,9 @@ checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" [[package]] name = "humantime" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "hyper" @@ -1563,13 +1566,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] -name = "memmap" -version = "0.7.0" +name = "memmap2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" dependencies = [ "libc", - "winapi 0.3.9", ] [[package]] @@ -1629,12 +1631,25 @@ dependencies = [ "kernel32-sys", "libc", "log", - "miow", + "miow 0.2.2", "net2", "slab", "winapi 0.2.8", ] +[[package]] +name = "mio" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e50ae3f04d169fcc9bde0b547d1c205219b7157e07ded9c5aff03e0637cb3ed7" +dependencies = [ + "libc", + "log", + "miow 0.3.6", + "ntapi", + "winapi 0.3.9", +] + [[package]] name = "mio-uds" version = "0.6.8" @@ -1643,7 +1658,7 @@ checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", - "mio", + "mio 0.6.23", ] [[package]] @@ -1658,6 +1673,16 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi 0.3.9", +] + [[package]] name = "multimap" version = "0.8.2" @@ -1684,9 +1709,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.36" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cf75f38f16cb05ea017784dc6dbfd354f76c223dba37701734c4f5a9337d02" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ "cfg-if 0.1.10", "libc", @@ -1695,15 +1720,23 @@ dependencies = [ [[package]] name = "nix" -version = "0.17.0" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" dependencies = [ "bitflags", "cc", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "void", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi 0.3.9", ] [[package]] @@ -2179,12 +2212,6 @@ dependencies = [ "prost", ] -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "0.6.13" @@ -2653,6 +2680,15 @@ dependencies = [ "opaque-debug 0.3.0", ] +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.2.2" @@ -2694,9 +2730,9 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fa579ccae25e66c5b9f3e058b16b19efc823e653ee491bbcda1f06a440de7a" +checksum = "469a64e65f2788c0fcd68124fce6a490c139a37ee159d855fe45bde9404be96e" dependencies = [ "Inflector", "base64 0.12.3", @@ -2718,9 +2754,9 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e908b0267c02e3898fb5769109dddbc936f14aadcedd3d5925d1a4d9b120f9" +checksum = "5bc41382ae2e834acce6b26e361e0ba79acec3b6633e36189e1cbcd16e3d9edf" dependencies = [ "chrono", "clap", @@ -2734,9 +2770,9 @@ dependencies = [ [[package]] name = "solana-cli-config" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0f69ef61a624038c6c2e0109f08c827633a04a4d21272e1e02e3bd70961008" +checksum = "ef627ff85612a66af08384dfbe006948c74143cee6a0b80311a94e5e0ffe8c2b" dependencies = [ "dirs-next", "lazy_static", @@ -2748,9 +2784,9 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ee3b369812df3c30c9076ac2d5f042e09d7cb9c04e2928ebf5df26ad4ab2" +checksum = "3fbe10a0b09201e4d272848644720d75d237dfe92536701b973ecb6a042d8363" dependencies = [ "base64 0.13.0", "bincode", @@ -2759,6 +2795,7 @@ dependencies = [ "indicatif", "jsonrpc-core", "log", + "net2", "rayon", "reqwest", "semver 0.11.0", @@ -2779,9 +2816,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5c599f4049337d8c89c08811b3044203d083038e4e4be724843088211f3158" +checksum = "d0dbb6f55cfd7eefb7d739c3a5b8ff2bba53825f002b7a3fc0531c2a335092a5" dependencies = [ "bincode", "chrono", @@ -2793,9 +2830,9 @@ dependencies = [ [[package]] name = "solana-crate-features" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc73ada8cddc62edf58dc09ef3cf4551259d0ca1c7ece5f102757279ec2afa95" +checksum = "a694ee719e16262801bdaeacda065e21352b7607cd7327b180172adca99864dc" dependencies = [ "backtrace", "bytes 0.4.12", @@ -2817,13 +2854,12 @@ dependencies = [ [[package]] name = "solana-faucet" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16cae7630ea7accaae2e6f538f400bcac312271cf4ab882a92875570447650b0" +checksum = "ca85d93e05c4270f15c4ce929895bafa42a6d6f368d6d70ada93fbd51add69f8" dependencies = [ "bincode", "byteorder", - "bytes 0.4.12", "clap", "log", "serde", @@ -2834,21 +2870,20 @@ dependencies = [ "solana-metrics", "solana-sdk", "solana-version", - "tokio 0.1.22", - "tokio-codec", + "tokio 0.3.6", ] [[package]] name = "solana-frozen-abi" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5ab6ad3dda6a3d95d19603eeedc65689d8125eafb3e23c6a1e01ab8a6ba60c" +checksum = "2cc29ffc04200709ae0ece6a6a4a78d97043537e0cf70fa36a0cec200140e893" dependencies = [ "bs58", "bv", "generic-array 0.14.4", "log", - "memmap", + "memmap2", "rustc_version", "serde", "serde_derive", @@ -2860,9 +2895,9 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffaa09aa938a67501479ed8a785071c6993f72c34e43f680db3ea7a2dadad9e7" +checksum = "9351c5355512afadaa64225d0653b050c768a7d5938429bff8a2e7c17143429f" dependencies = [ "lazy_static", "proc-macro2 1.0.24", @@ -2873,9 +2908,9 @@ dependencies = [ [[package]] name = "solana-logger" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d949157d0b23eaf5758b427d90741d2a90751c4e3dfee028f5726ab8b36e769" +checksum = "188a6e6d7e001336d00ece746ecbd4b5e5ff1a12397c456934d831288f99b23d" dependencies = [ "env_logger", "lazy_static", @@ -2884,9 +2919,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c197e92932f6498371df4a52e972403b0a3dfd3eac101b2844774b43292d89" +checksum = "cef4e2d6ffd08f35e6268b1bcffe63b37a6f9a53d91644757d607b8d3ba4251e" dependencies = [ "jemalloc-ctl", "jemallocator", @@ -2897,9 +2932,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ed2f570bd9d87a991c58800bd94149c34243f14c6b3dfc39fbca602e13db70a" +checksum = "4c02403e4f6fcb97e149854c42a0a7e81af041e5228544ad375cd75fb82a859d" dependencies = [ "env_logger", "gethostname", @@ -2911,12 +2946,11 @@ dependencies = [ [[package]] name = "solana-net-utils" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0a40ca7c0f5be26d1a203ac1ef44920f7c845ffdcf922e2aa597662e13958" +checksum = "5468c5a5590aef624a668d264cb64f009d0d81ad8c7d609a57dbcb4328fcb2ef" dependencies = [ "bincode", - "bytes 0.4.12", "clap", "log", "nix", @@ -2927,15 +2961,15 @@ dependencies = [ "solana-clap-utils", "solana-logger", "solana-version", - "tokio 0.1.22", + "tokio 0.3.6", "url", ] [[package]] name = "solana-program" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9c6cb16e8aa986bc0d2af8ec50628f7451bef9dac89924adf48302bd4fc755" +checksum = "6c16bdd751d5549716a610f87b8ab1ca13ceaf66dfc9f325440894a72eadb74e" dependencies = [ "bincode", "bs58", @@ -2963,9 +2997,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de7367bfa559a08c2023d62f780ae01fddf865b974d7fd449bbeb1399641764d" +checksum = "72de795cac4d5e9c9c083177bfcbe343c461c1ce5dcffbecddd2d0b7f09e04fa" dependencies = [ "lazy_static", "num_cpus", @@ -2973,9 +3007,9 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fde6cc84dfcc15c34eedba0675ca51cbaa8b83ca70233c5751459af869568e1" +checksum = "46f7717eca656d386dd233c60d6c63c3d5e16fa64dd3bca1a7f1b27007cf354e" dependencies = [ "base32", "console 0.11.3", @@ -2993,9 +3027,9 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1364382b1ea6dd516c53455c817eaa5cd47bf7565cd00953586002731dd8fd" +checksum = "b817d921c53245e3fcc78ac605fb61ae0ada81346fda7dec41a7681770610ca5" dependencies = [ "bincode", "blake3", @@ -3013,7 +3047,7 @@ dependencies = [ "libc", "libloading", "log", - "memmap", + "memmap2", "num-derive 0.3.3", "num-traits", "num_cpus", @@ -3044,9 +3078,9 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c38a02d501422070cd6a4d561b4ab1408e311c5a0b5af3a7bb01246da14f66" +checksum = "280fa9cef4fdb9154fea538ed1efdc74215b3734a4ad611ddb1393269efac1bb" dependencies = [ "assert_matches", "bincode", @@ -3063,7 +3097,7 @@ dependencies = [ "lazy_static", "libsecp256k1", "log", - "memmap", + "memmap2", "num-derive 0.3.3", "num-traits", "pbkdf2", @@ -3088,9 +3122,9 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475a680cd175f2e256452e007c6f8622d3a1ab97ab36d26303b35576e24f340c" +checksum = "11489c157e78f60ad9d4a96dc4d1955ef6936ef606f6ce0273689fcdb90391c2" dependencies = [ "bs58", "proc-macro2 1.0.24", @@ -3101,9 +3135,9 @@ dependencies = [ [[package]] name = "solana-secp256k1-program" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f76265997062d8cb18b8055ab200294087ee0bb2de3cf65864eb964f39b2c8" +checksum = "3eb1617ea1dec65e59ab94eb6bdcf15e831270ae41317f4e9bee926693f74f30" dependencies = [ "bincode", "digest 0.9.0", @@ -3116,9 +3150,9 @@ dependencies = [ [[package]] name = "solana-stake-program" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fcb16ef12c9ed669308074bcc0549d5b36f29597fb701530f108088d438b3cd" +checksum = "a691ac55bb8cb4ded6c192ab117b545e3090e92c13560b02318b02ed423eaef6" dependencies = [ "bincode", "log", @@ -3138,9 +3172,9 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f69cb54e6854d4799529c22c21c1d53abaf2ebfc7b2c2fe1ce84b2afb9c620" +checksum = "ede0f19e5bec9b0d9a85c4eba2f453547064b124b4849ddfe6e7212a94aa75b0" dependencies = [ "Inflector", "base64 0.12.3", @@ -3162,9 +3196,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6995a2a6734a108b4a80497a391e86c8d234d94b02ceec0cebc9ba132874502" +checksum = "94bc42af0fb331572e8161e099f4b1c50cec8f12c3d64d61ff04c0719199aebb" dependencies = [ "log", "rustc_version", @@ -3178,9 +3212,9 @@ dependencies = [ [[package]] name = "solana-vote-program" -version = "1.4.14" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b42b3fd7b76dd8c98eb13f92677b1803d0bc1ac06af45e10f1aee46b8c19a8a" +checksum = "c9a3c8a54916bb189a80dee075bae93950198ac8b51cde6d2e7e8860b8a05250" dependencies = [ "bincode", "log", @@ -3205,18 +3239,18 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spl-memo" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99775feb54f735a6826ea0af500c1f78f7a5974d6b17f1ac586cd114e2da7d80" +checksum = "fb2b771f6146dec14ef5fbf498f9374652c54badc3befc8c40c1d426dd45d720" dependencies = [ "solana-program", ] [[package]] name = "spl-token" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f77fa0b41cbc82d1d7c8f2d914b49e9a1a7b6e32af952d03383fb989c42bc89" +checksum = "a9774eebb62ff1ff2f5eca112413e476143925a2f5a43cee98fc5d3a6c0eec5c" dependencies = [ "arrayref", "num-derive 0.3.3", @@ -3447,7 +3481,7 @@ checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6" dependencies = [ "bytes 0.4.12", "futures 0.1.30", - "mio", + "mio 0.6.23", "num_cpus", "tokio-codec", "tokio-current-thread", @@ -3476,12 +3510,34 @@ dependencies = [ "lazy_static", "libc", "memchr", - "mio", + "mio 0.6.23", "mio-uds", "num_cpus", "pin-project-lite 0.1.11", "slab", - "tokio-macros", + "tokio-macros 0.2.6", +] + +[[package]] +name = "tokio" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "720ba21c25078711bf456d607987d95bce90f7c3bea5abe1db587862e7a1e87c" +dependencies = [ + "autocfg", + "bytes 0.6.0", + "futures-core", + "libc", + "memchr", + "mio 0.7.7", + "num_cpus", + "once_cell", + "parking_lot 0.11.1", + "pin-project-lite 0.2.0", + "signal-hook-registry", + "slab", + "tokio-macros 0.3.2", + "winapi 0.3.9", ] [[package]] @@ -3548,6 +3604,17 @@ dependencies = [ "syn 1.0.53", ] +[[package]] +name = "tokio-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46dfffa59fc3c8aad216ed61bdc2c263d2b9d87a9c8ac9de0c11a813e51b6db7" +dependencies = [ + "proc-macro2 1.0.24", + "quote 1.0.7", + "syn 1.0.53", +] + [[package]] name = "tokio-reactor" version = "0.1.12" @@ -3558,7 +3625,7 @@ dependencies = [ "futures 0.1.30", "lazy_static", "log", - "mio", + "mio 0.6.23", "num_cpus", "parking_lot 0.9.0", "slab", @@ -3598,7 +3665,7 @@ dependencies = [ "bytes 0.4.12", "futures 0.1.30", "iovec", - "mio", + "mio 0.6.23", "tokio-io", "tokio-reactor", ] @@ -3641,7 +3708,7 @@ dependencies = [ "bytes 0.4.12", "futures 0.1.30", "log", - "mio", + "mio 0.6.23", "tokio-codec", "tokio-io", "tokio-reactor", @@ -3658,7 +3725,7 @@ dependencies = [ "iovec", "libc", "log", - "mio", + "mio 0.6.23", "mio-uds", "tokio-codec", "tokio-io", @@ -4114,12 +4181,6 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - [[package]] name = "walkdir" version = "2.3.1" diff --git a/solana/agent/Cargo.toml b/solana/agent/Cargo.toml index f1c75d17..81bb80df 100644 --- a/solana/agent/Cargo.toml +++ b/solana/agent/Cargo.toml @@ -9,10 +9,10 @@ tonic = "0.3.0" tokio = { version = "0.2", features = ["rt-threaded", "time", "stream", "fs", "macros", "uds"] } prost = "0.6" prost-types = "0.6" -solana-sdk = { version = "1.4.7" } -solana-client = { version = "1.4.7" } -solana-faucet = "1.4.7" -spl-token = "=3.0.0" +solana-sdk = { version = "1.4.20" } +solana-client = { version = "1.4.20" } +solana-faucet = "1.4.20" +spl-token = "=3.0.1" wormhole-bridge = { path = "../bridge" } primitive-types = { version = "0.7.2" } hex = "0.4.2" @@ -21,7 +21,7 @@ tungstenite = "0.11.1" serde = "1.0.103" url = "2.1.1" serde_bytes = "0.11.5" -log = "0.4.8" +log = "0.4.11" serde_derive = "1.0.103" serde_json = "1.0.57" bs58 = "0.3.1" diff --git a/solana/bridge/Cargo.toml b/solana/bridge/Cargo.toml index 517e729f..b2bcd757 100644 --- a/solana/bridge/Cargo.toml +++ b/solana/bridge/Cargo.toml @@ -17,8 +17,8 @@ program = ["spl-token/no-entrypoint"] num-derive = "0.2" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-program = "1.4.7" -spl-token = { version = "=3.0.0" } +solana-program = "1.4.20" +spl-token = { version = "=3.0.1" } thiserror = "1.0" byteorder = "1.3.4" zerocopy = "0.3.0" diff --git a/solana/bridge/src/error.rs b/solana/bridge/src/error.rs index f0f92b9b..356d58fe 100644 --- a/solana/bridge/src/error.rs +++ b/solana/bridge/src/error.rs @@ -112,6 +112,9 @@ pub enum Error { /// Invalid Sysvar #[error("InvalidSysvar")] InvalidSysvar, + /// Invalid Chain + #[error("InvalidChain")] + InvalidChain, } impl From for ProgramError { diff --git a/solana/bridge/src/error_program.rs b/solana/bridge/src/error_program.rs index be44dbc6..40151b91 100644 --- a/solana/bridge/src/error_program.rs +++ b/solana/bridge/src/error_program.rs @@ -42,6 +42,7 @@ impl PrintProgramError for Error { Error::InsufficientFees => msg!("Error: InsufficientFees"), Error::InvalidOwner => msg!("Error: InvalidOwner"), Error::InvalidSysvar => msg!("Error: InvalidSysvar"), + Error::InvalidChain => msg!("Error: InvalidChain"), } } } diff --git a/solana/bridge/src/instruction.rs b/solana/bridge/src/instruction.rs index 02701766..d9c94c1b 100644 --- a/solana/bridge/src/instruction.rs +++ b/solana/bridge/src/instruction.rs @@ -425,6 +425,12 @@ pub fn post_vaa( Bridge::derive_guardian_set_id(program_id, &bridge_key, u.new_index)?; accounts.push(AccountMeta::new(guardian_set_key, false)); } + VAABody::UpgradeContract(u) => { + accounts.push(AccountMeta::new(u.buffer, false)); + let (programdata_address, _) = Pubkey::find_program_address(&[program_id.as_ref()], &solana_program::bpf_loader_upgradeable::id()); + accounts.push(AccountMeta::new(programdata_address, false)); + accounts.push(AccountMeta::new_readonly(solana_program::bpf_loader_upgradeable::id(), false)); + } VAABody::Transfer(t) => { if t.source_chain == CHAIN_ID_SOLANA { // Solana (any) -> Ethereum (any) diff --git a/solana/bridge/src/processor.rs b/solana/bridge/src/processor.rs index 9d84d939..a9586f55 100644 --- a/solana/bridge/src/processor.rs +++ b/solana/bridge/src/processor.rs @@ -36,6 +36,7 @@ use solana_program::program_pack::Pack; use std::borrow::BorrowMut; use std::ops::Add; use solana_program::fee_calculator::FeeCalculator; +use crate::vaa::BodyContractUpgrade; /// SigInfo contains metadata about signers in a VerifySignature ix struct SigInfo { @@ -772,6 +773,19 @@ impl Bridge { ) } } + VAABody::UpgradeContract(v) => { + if v.chain_id == CHAIN_ID_SOLANA { + evict_signatures = true; + Self::process_vaa_upgrade( + program_id, + accounts, + bridge_info, + v, + ) + } else { + return Err(Error::InvalidChain.into()); + } + } }?; // Check and create claim @@ -1001,6 +1015,25 @@ impl Bridge { Ok(()) } + /// Processes a VAA contract upgrade + pub fn process_vaa_upgrade( + program_id: &Pubkey, + accounts: &[AccountInfo], + bridge_info: &AccountInfo, + b: &BodyContractUpgrade, + ) -> ProgramResult { + // Invoke upgrade + let upgrade_ix = solana_program::bpf_loader_upgradeable::upgrade( + program_id, + &b.buffer, + bridge_info.key, + bridge_info.key, + ); + Self::invoke_as_bridge(program_id, &upgrade_ix, accounts); + + Ok(()) + } + /// Creates a new wrapped asset pub fn process_create_wrapped( program_id: &Pubkey, diff --git a/solana/bridge/src/vaa.rs b/solana/bridge/src/vaa.rs index eaa9783b..f080de5e 100644 --- a/solana/bridge/src/vaa.rs +++ b/solana/bridge/src/vaa.rs @@ -6,6 +6,7 @@ use sha3::Digest; use solana_program::program_error::ProgramError; use crate::{error::Error, state::AssetMeta}; +use solana_program::pubkey::Pubkey; pub type ForeignAddress = [u8; 32]; @@ -129,12 +130,14 @@ impl VAA { pub enum VAABody { UpdateGuardianSet(BodyUpdateGuardianSet), Transfer(BodyTransfer), + UpgradeContract(BodyContractUpgrade), } impl VAABody { fn action_id(&self) -> u8 { match self { VAABody::UpdateGuardianSet(_) => 0x01, + VAABody::UpgradeContract(_) => 0x02, VAABody::Transfer(_) => 0x10, } } @@ -147,6 +150,7 @@ impl VAABody { 0x01 => { VAABody::UpdateGuardianSet(BodyUpdateGuardianSet::deserialize(&mut payload_data)?) } + 0x02 => VAABody::UpgradeContract(BodyContractUpgrade::deserialize(&mut payload_data)?), 0x10 => VAABody::Transfer(BodyTransfer::deserialize(&mut payload_data)?), _ => { return Err(Error::InvalidVAAAction); @@ -160,6 +164,7 @@ impl VAABody { match self { VAABody::Transfer(b) => b.serialize(), VAABody::UpdateGuardianSet(b) => b.serialize(), + VAABody::UpgradeContract(b) => b.serialize(), } } } @@ -181,6 +186,34 @@ pub struct BodyTransfer { pub amount: U256, } +#[derive(Clone, Debug, PartialEq)] +pub struct BodyContractUpgrade { + pub chain_id: u8, + pub buffer: Pubkey, +} + +impl BodyContractUpgrade { + fn deserialize(data: &mut Cursor<&Vec>) -> Result { + let chain_id = data.read_u8()?; + let mut key: [u8; 32] = [0; 32]; + data.read(&mut key[..])?; + + Ok(BodyContractUpgrade { + chain_id, + buffer: Pubkey::new(&key[..]), + }) + } + + fn serialize(&self) -> Result, Error> { + let mut v: Cursor> = Cursor::new(Vec::new()); + v.write_u8(self.chain_id)?; + v.write(&self.buffer.to_bytes()); + + Ok(v.into_inner()) + } +} + + impl BodyUpdateGuardianSet { fn deserialize(data: &mut Cursor<&Vec>) -> Result { let new_index = data.read_u32::()?; @@ -273,6 +306,8 @@ mod tests { state::AssetMeta, vaa::{BodyTransfer, BodyUpdateGuardianSet, Signature, VAABody, VAA}, }; + use crate::vaa::BodyContractUpgrade; + use solana_program::pubkey::Pubkey; #[test] fn serialize_deserialize_vaa_transfer() { @@ -329,6 +364,29 @@ mod tests { assert_eq!(vaa, parsed_vaa) } + #[test] + fn serialize_deserialize_vaa_contract_upgrade() { + let vaa = VAA { + version: 8, + guardian_set_index: 3, + signatures: vec![Signature { + index: 1, + r: [2; 32], + s: [2; 32], + v: 7, + }], + timestamp: 83, + payload: Some(VAABody::UpgradeContract(BodyContractUpgrade { + chain_id: 3, + buffer: Pubkey::new_unique(), + })), + }; + + let data = vaa.serialize().unwrap(); + let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap(); + assert_eq!(vaa, parsed_vaa) + } + #[test] fn parse_given_guardian_set_update() { let vaa = VAA { @@ -411,4 +469,36 @@ mod tests { let rec_data = parsed_vaa.serialize().unwrap(); assert_eq!(data, rec_data); } + + #[test] + fn parse_given_contract_upgrade() { + let vaa = VAA { + version: 1, + guardian_set_index: 2, + signatures: vec![Signature { + index: 0, + r: [ + 72, 156, 56, 20, 222, 146, 161, 112, 22, 97, 69, 59, 188, 199, 130, 240, 89, + 249, 241, 79, 96, 27, 235, 10, 99, 16, 56, 80, 232, 188, 235, 11 + ], + s: [ + 65, 19, 144, 42, 104, 122, 52, 0, 126, 7, 43, 127, 120, 85, 5, 21, 216, 207, + 78, 73, 213, 207, 142, 103, 211, 192, 100, 90, 27, 98, 176, 98 + ], + v: 1, + }], + timestamp: 4000, + payload: Some(VAABody::UpgradeContract(BodyContractUpgrade { + chain_id: 2, + buffer: Pubkey::new(&[146, 115, 122, 21, 4, 243, 179, 223, 140, 147, 203, 133, 198, 74, 72, 96, 187, + 39, 14, 38, 2, 107, 110, 55, 240, 149, 53, 106, 64, 111, 106, 244]), + })), + }; + let data = hex::decode("01000000020100489c3814de92a1701661453bbcc782f059f9f14f601beb0a63103850e8bceb0b4113902a687a34007e072b7f78550515d8cf4e49d5cf8e67d3c0645a1b62b0620100000fa0020292737a1504f3b3df8c93cb85c64a4860bb270e26026b6e37f095356a406f6af4").unwrap(); + let parsed_vaa = VAA::deserialize(data.as_slice()).unwrap(); + assert_eq!(vaa, parsed_vaa); + + let rec_data = parsed_vaa.serialize().unwrap(); + assert_eq!(data, rec_data); + } } diff --git a/solana/cli/Cargo.toml b/solana/cli/Cargo.toml index 29057147..2e2f4e77 100644 --- a/solana/cli/Cargo.toml +++ b/solana/cli/Cargo.toml @@ -8,14 +8,14 @@ edition = "2018" [dependencies] clap = "2.33.0" -solana-clap-utils = { version = "1.4.7" } -solana-cli-config = { version = "1.4.7" } -solana-logger = { version = "1.4.7" } -solana-sdk = { version = "1.4.7" } -solana-client = { version = "1.4.7" } -solana-faucet = "1.4.7" -solana-account-decoder = { version = "1.4.7" } -spl-token = "=3.0.0" +solana-clap-utils = { version = "1.4.20" } +solana-cli-config = { version = "1.4.20" } +solana-logger = { version = "1.4.20" } +solana-sdk = { version = "1.4.20" } +solana-client = { version = "1.4.20" } +solana-faucet = "1.4.20" +solana-account-decoder = { version = "1.4.20" } +spl-token = "=3.0.1" wormhole-bridge = { path = "../bridge" } primitive-types = { version = "0.7.2" } hex = "0.4.2" diff --git a/third_party/solana/Dockerfile b/third_party/solana/Dockerfile index 764f39a1..4d16707d 100644 --- a/third_party/solana/Dockerfile +++ b/third_party/solana/Dockerfile @@ -11,7 +11,7 @@ RUN rustup component add rustfmt WORKDIR /usr/src/solana RUN git clone https://github.com/solana-labs/solana --branch master && \ - cd solana && git checkout v1.4.12 + cd solana && git checkout v1.4.20 ADD *.patch .