From 2b624ab1e8d44b49ffcb21637ac2214aeafd681f Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Thu, 3 Oct 2024 13:41:32 -0400 Subject: [PATCH] Fix disableSubnetValidatorTx --- vms/platformvm/block/executor/manager.go | 17 +- vms/platformvm/network/gossip.go | 2 +- vms/platformvm/txs/codec.go | 1 + .../txs/disable_subnet_validator_tx.go | 2 +- .../txs/executor/standard_tx_executor.go | 2 +- vms/platformvm/warp/message/payload.go | 2 +- .../warp/message/register_subnet_validator.go | 2 +- .../warp/message/subnet_conversion.go | 2 +- .../message/subnet_validator_registration.go | 2 +- .../warp/message/subnet_validator_weight.go | 2 +- .../primary/examples/convert-subnet/main.go | 2 +- .../examples/disable-subnet-validator/main.go | 54 +++++ .../primary/examples/increase-balance/main.go | 56 +++++ .../register-subnet-validator/main.go | 20 +- .../set-subnet-validator-weight/main.go | 42 +++- .../examples/sign-subnet-conversion/main.go | 119 +++++++++++ .../main.go | 106 +++++----- .../sign-subnet-validator-removal/main.go | 196 ++++++++++++++++++ .../sign-subnet-validator-weight/main.go | 127 ++++++++++++ wallet/subnet/primary/wallet.go | 6 +- 20 files changed, 680 insertions(+), 82 deletions(-) create mode 100644 wallet/subnet/primary/examples/disable-subnet-validator/main.go create mode 100644 wallet/subnet/primary/examples/increase-balance/main.go create mode 100644 wallet/subnet/primary/examples/sign-subnet-conversion/main.go create mode 100644 wallet/subnet/primary/examples/sign-subnet-validator-removal/main.go create mode 100644 wallet/subnet/primary/examples/sign-subnet-validator-weight/main.go diff --git a/vms/platformvm/block/executor/manager.go b/vms/platformvm/block/executor/manager.go index 2954671a5b8..a607926c913 100644 --- a/vms/platformvm/block/executor/manager.go +++ b/vms/platformvm/block/executor/manager.go @@ -6,6 +6,7 @@ package executor import ( "context" "errors" + "fmt" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/consensus/snowman" @@ -125,7 +126,7 @@ func (m *manager) VerifyTx(tx *txs.Tx) error { recommendedPChainHeight, err := m.ctx.ValidatorState.GetMinimumHeight(context.TODO()) if err != nil { - return err + return fmt.Errorf("failed to fetch P-chain height: %w", err) } err = executor.VerifyWarpMessages( context.TODO(), @@ -135,12 +136,12 @@ func (m *manager) VerifyTx(tx *txs.Tx) error { tx.Unsigned, ) if err != nil { - return err + return fmt.Errorf("failed verifying warp messages: %w", err) } stateDiff, err := state.NewDiff(m.preferred, m) if err != nil { - return err + return fmt.Errorf("failed creating state diff: %w", err) } nextBlkTime, _, err := state.NextBlockTime( @@ -149,21 +150,25 @@ func (m *manager) VerifyTx(tx *txs.Tx) error { m.txExecutorBackend.Clk, ) if err != nil { - return err + return fmt.Errorf("failed selecting next block time: %w", err) } _, err = executor.AdvanceTimeTo(m.txExecutorBackend, stateDiff, nextBlkTime) if err != nil { - return err + return fmt.Errorf("failed to advance the chain time: %w", err) } feeCalculator := state.PickFeeCalculator(m.txExecutorBackend.Config, stateDiff) - return tx.Unsigned.Visit(&executor.StandardTxExecutor{ + err = tx.Unsigned.Visit(&executor.StandardTxExecutor{ Backend: m.txExecutorBackend, State: stateDiff, FeeCalculator: feeCalculator, Tx: tx, }) + if err != nil { + return fmt.Errorf("failed execution: %w", err) + } + return nil } func (m *manager) VerifyUniqueInputs(blkID ids.ID, inputs set.Set[ids.ID]) error { diff --git a/vms/platformvm/network/gossip.go b/vms/platformvm/network/gossip.go index 7e6e7adc341..43e730b90e3 100644 --- a/vms/platformvm/network/gossip.go +++ b/vms/platformvm/network/gossip.go @@ -109,7 +109,7 @@ func (g *gossipMempool) Add(tx *txs.Tx) error { if err := g.txVerifier.VerifyTx(tx); err != nil { g.Mempool.MarkDropped(txID, err) - return err + return fmt.Errorf("failed verification: %w", err) } if err := g.Mempool.Add(tx); err != nil { diff --git a/vms/platformvm/txs/codec.go b/vms/platformvm/txs/codec.go index dbe84b196a1..e3b4539e91e 100644 --- a/vms/platformvm/txs/codec.go +++ b/vms/platformvm/txs/codec.go @@ -126,5 +126,6 @@ func RegisterEtnaTypes(targetCodec linearcodec.Codec) error { targetCodec.RegisterType(&RegisterSubnetValidatorTx{}), targetCodec.RegisterType(&SetSubnetValidatorWeightTx{}), targetCodec.RegisterType(&IncreaseBalanceTx{}), + targetCodec.RegisterType(&DisableSubnetValidatorTx{}), ) } diff --git a/vms/platformvm/txs/disable_subnet_validator_tx.go b/vms/platformvm/txs/disable_subnet_validator_tx.go index ca03f420864..08e59ed1fa5 100644 --- a/vms/platformvm/txs/disable_subnet_validator_tx.go +++ b/vms/platformvm/txs/disable_subnet_validator_tx.go @@ -15,7 +15,7 @@ type DisableSubnetValidatorTx struct { // Metadata, inputs and outputs BaseTx `serialize:"true"` // ID corresponding to the validator - ValidationID ids.ID `json:"validationID"` + ValidationID ids.ID `serialize:"true" json:"validationID"` // Authorizes this validator to be disabled DisableAuth verify.Verifiable `serialize:"true" json:"disableAuthorization"` } diff --git a/vms/platformvm/txs/executor/standard_tx_executor.go b/vms/platformvm/txs/executor/standard_tx_executor.go index f01e493730b..243f9f92597 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor.go +++ b/vms/platformvm/txs/executor/standard_tx_executor.go @@ -1004,7 +1004,7 @@ func (e *StandardTxExecutor) DisableSubnetValidatorTx(tx *txs.DisableSubnetValid sov, err := e.State.GetSubnetOnlyValidator(tx.ValidationID) if err != nil { - return err + return fmt.Errorf("couldn't load SoV for %s: %w", tx.ValidationID, err) } var disableOwner message.PChainOwner diff --git a/vms/platformvm/warp/message/payload.go b/vms/platformvm/warp/message/payload.go index 6903cc22001..1da0f71aa9b 100644 --- a/vms/platformvm/warp/message/payload.go +++ b/vms/platformvm/warp/message/payload.go @@ -43,7 +43,7 @@ func Parse(bytes []byte) (Payload, error) { return p, nil } -func initialize(p Payload) error { +func Initialize(p Payload) error { bytes, err := Codec.Marshal(CodecVersion, &p) if err != nil { return fmt.Errorf("couldn't marshal %T payload: %w", p, err) diff --git a/vms/platformvm/warp/message/register_subnet_validator.go b/vms/platformvm/warp/message/register_subnet_validator.go index cf0b1cbcd56..ba4eb3aab16 100644 --- a/vms/platformvm/warp/message/register_subnet_validator.go +++ b/vms/platformvm/warp/message/register_subnet_validator.go @@ -99,7 +99,7 @@ func NewRegisterSubnetValidator( DisableOwner: disableOwner, Weight: weight, } - return msg, initialize(msg) + return msg, Initialize(msg) } // ParseRegisterSubnetValidator parses bytes into an initialized diff --git a/vms/platformvm/warp/message/subnet_conversion.go b/vms/platformvm/warp/message/subnet_conversion.go index a93354fbe44..f1af8e78e5a 100644 --- a/vms/platformvm/warp/message/subnet_conversion.go +++ b/vms/platformvm/warp/message/subnet_conversion.go @@ -50,7 +50,7 @@ func NewSubnetConversion(id ids.ID) (*SubnetConversion, error) { msg := &SubnetConversion{ ID: id, } - return msg, initialize(msg) + return msg, Initialize(msg) } // ParseSubnetConversion parses bytes into an initialized SubnetConversion. diff --git a/vms/platformvm/warp/message/subnet_validator_registration.go b/vms/platformvm/warp/message/subnet_validator_registration.go index 2ab46c88dc3..f0e1919b1e2 100644 --- a/vms/platformvm/warp/message/subnet_validator_registration.go +++ b/vms/platformvm/warp/message/subnet_validator_registration.go @@ -34,7 +34,7 @@ func NewSubnetValidatorRegistration( ValidationID: validationID, Registered: registered, } - return msg, initialize(msg) + return msg, Initialize(msg) } // ParseSubnetValidatorRegistration parses bytes into an initialized diff --git a/vms/platformvm/warp/message/subnet_validator_weight.go b/vms/platformvm/warp/message/subnet_validator_weight.go index dcfa6c5a16c..5b94889a8f0 100644 --- a/vms/platformvm/warp/message/subnet_validator_weight.go +++ b/vms/platformvm/warp/message/subnet_validator_weight.go @@ -46,7 +46,7 @@ func NewSubnetValidatorWeight( Nonce: nonce, Weight: weight, } - return msg, initialize(msg) + return msg, Initialize(msg) } // ParseSubnetValidatorWeight parses bytes into an initialized diff --git a/wallet/subnet/primary/examples/convert-subnet/main.go b/wallet/subnet/primary/examples/convert-subnet/main.go index b7133488f6e..f78e0cd92b3 100644 --- a/wallet/subnet/primary/examples/convert-subnet/main.go +++ b/wallet/subnet/primary/examples/convert-subnet/main.go @@ -24,7 +24,7 @@ func main() { uri := "http://localhost:9700" kc := secp256k1fx.NewKeychain(key) subnetID := ids.FromStringOrPanic("2DeHa7Qb6sufPkmQcFWG2uCd4pBPv9WB6dkzroiMQhd1NSRtof") - chainID := ids.FromStringOrPanic("E8nTR9TtRwfkS7XFjTYUYHENQ91mkPMtDUwwCeu7rNgBBtkqu") + chainID := ids.FromStringOrPanic("2BMFrJ9xeh5JdwZEx6uuFcjfZC2SV2hdbMT8ee5HrvjtfJb5br") addressHex := "" weight := units.Schmeckle diff --git a/wallet/subnet/primary/examples/disable-subnet-validator/main.go b/wallet/subnet/primary/examples/disable-subnet-validator/main.go new file mode 100644 index 00000000000..cb2030ef330 --- /dev/null +++ b/wallet/subnet/primary/examples/disable-subnet-validator/main.go @@ -0,0 +1,54 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "log" + "time" + + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +func main() { + key := genesis.EWOQKey + uri := primary.LocalAPIURI + kc := secp256k1fx.NewKeychain(key) + validationID := ids.FromStringOrPanic("9FAftNgNBrzHUMMApsSyV6RcFiL9UmCbvsCu28xdLV2mQ7CMo") + + ctx := context.Background() + + // MakeWallet fetches the available UTXOs owned by [kc] on the network that + // [uri] is hosting and registers [subnetID]. + walletSyncStartTime := time.Now() + wallet, err := primary.MakeWallet(ctx, &primary.WalletConfig{ + URI: uri, + AVAXKeychain: kc, + EthKeychain: kc, + ValidationIDs: []ids.ID{validationID}, + }) + if err != nil { + log.Fatalf("failed to initialize wallet: %s\n", err) + } + log.Printf("synced wallet in %s\n", time.Since(walletSyncStartTime)) + + // Get the P-chain wallet + pWallet := wallet.P() + + disableSubnetValidatorStartTime := time.Now() + disableSubnetValidatorTx, err := pWallet.IssueDisableSubnetValidatorTx( + validationID, + ) + if err != nil { + log.Fatalf("failed to issue disable subnet validator transaction: %s\n", err) + } + log.Printf("disabled %s with %s in %s\n", + validationID, + disableSubnetValidatorTx.ID(), + time.Since(disableSubnetValidatorStartTime), + ) +} diff --git a/wallet/subnet/primary/examples/increase-balance/main.go b/wallet/subnet/primary/examples/increase-balance/main.go new file mode 100644 index 00000000000..e0a1af0c109 --- /dev/null +++ b/wallet/subnet/primary/examples/increase-balance/main.go @@ -0,0 +1,56 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "log" + "time" + + "github.com/ava-labs/avalanchego/genesis" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" +) + +func main() { + key := genesis.EWOQKey + uri := primary.LocalAPIURI + kc := secp256k1fx.NewKeychain(key) + validationID := ids.FromStringOrPanic("9FAftNgNBrzHUMMApsSyV6RcFiL9UmCbvsCu28xdLV2mQ7CMo") + balance := uint64(2) + + ctx := context.Background() + + // MakeWallet fetches the available UTXOs owned by [kc] on the network that + // [uri] is hosting and registers [subnetID]. + walletSyncStartTime := time.Now() + wallet, err := primary.MakeWallet(ctx, &primary.WalletConfig{ + URI: uri, + AVAXKeychain: kc, + EthKeychain: kc, + }) + if err != nil { + log.Fatalf("failed to initialize wallet: %s\n", err) + } + log.Printf("synced wallet in %s\n", time.Since(walletSyncStartTime)) + + // Get the P-chain wallet + pWallet := wallet.P() + + increaseBalanceStartTime := time.Now() + increaseBalanceTx, err := pWallet.IssueIncreaseBalanceTx( + validationID, + balance, + ) + if err != nil { + log.Fatalf("failed to issue increase balance transaction: %s\n", err) + } + log.Printf("increased balance of validationID %s by %d with %s in %s\n", + validationID, + balance, + increaseBalanceTx.ID(), + time.Since(increaseBalanceStartTime), + ) +} diff --git a/wallet/subnet/primary/examples/register-subnet-validator/main.go b/wallet/subnet/primary/examples/register-subnet-validator/main.go index 41c7f1661a9..05d51aa1719 100644 --- a/wallet/subnet/primary/examples/register-subnet-validator/main.go +++ b/wallet/subnet/primary/examples/register-subnet-validator/main.go @@ -15,7 +15,6 @@ import ( "github.com/ava-labs/avalanchego/genesis" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/crypto/bls" - "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/avalanchego/vms/platformvm/warp" @@ -30,9 +29,9 @@ func main() { uri := "http://localhost:9710" kc := secp256k1fx.NewKeychain(key) subnetID := ids.FromStringOrPanic("2DeHa7Qb6sufPkmQcFWG2uCd4pBPv9WB6dkzroiMQhd1NSRtof") - chainID := ids.FromStringOrPanic("21G9Uqg2R7hYa81kZK7oMCgstsHEiNGbkpB9kdBLUeWx3wWMsV") + chainID := ids.FromStringOrPanic("2BMFrJ9xeh5JdwZEx6uuFcjfZC2SV2hdbMT8ee5HrvjtfJb5br") addressHex := "" - weight := units.Schmeckle + weight := uint64(1) address, err := hex.DecodeString(addressHex) if err != nil { @@ -79,9 +78,11 @@ func main() { addressedCallPayload, err := message.NewRegisterSubnetValidator( subnetID, nodeID, - weight, nodePoP.PublicKey, uint64(time.Now().Add(5*time.Minute).Unix()), + message.PChainOwner{}, + message.PChainOwner{}, + weight, ) if err != nil { log.Fatalf("failed to create RegisterSubnetValidator message: %s\n", err) @@ -128,17 +129,16 @@ func main() { log.Fatalf("failed to create Warp message: %s\n", err) } - convertSubnetStartTime := time.Now() - addValidatorTx, err := pWallet.IssueRegisterSubnetValidatorTx( + registerSubnetValidatorStartTime := time.Now() + registerSubnetValidatorTx, err := pWallet.IssueRegisterSubnetValidatorTx( units.Avax, nodePoP.ProofOfPossession, - &secp256k1fx.OutputOwners{}, warp.Bytes(), ) if err != nil { - log.Fatalf("failed to issue add subnet validator transaction: %s\n", err) + log.Fatalf("failed to issue register subnet validator transaction: %s\n", err) } - var validationID ids.ID = hashing.ComputeHash256Array(addressedCallPayload.Bytes()) - log.Printf("added new subnet validator %s to subnet %s with txID %s as validationID %s in %s\n", nodeID, subnetID, addValidatorTx.ID(), validationID, time.Since(convertSubnetStartTime)) + validationID := addressedCallPayload.ValidationID() + log.Printf("registered new subnet validator %s to subnet %s with txID %s as validationID %s in %s\n", nodeID, subnetID, registerSubnetValidatorTx.ID(), validationID, time.Since(registerSubnetValidatorStartTime)) } diff --git a/wallet/subnet/primary/examples/set-subnet-validator-weight/main.go b/wallet/subnet/primary/examples/set-subnet-validator-weight/main.go index 082268505a6..35e7ce26410 100644 --- a/wallet/subnet/primary/examples/set-subnet-validator-weight/main.go +++ b/wallet/subnet/primary/examples/set-subnet-validator-weight/main.go @@ -6,11 +6,15 @@ package main import ( "context" "encoding/hex" + "encoding/json" "log" + "os" "time" "github.com/ava-labs/avalanchego/genesis" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/crypto/bls" + "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" @@ -22,17 +26,27 @@ func main() { key := genesis.EWOQKey uri := primary.LocalAPIURI kc := secp256k1fx.NewKeychain(key) - chainID := ids.FromStringOrPanic("2ko3NCPzHeneKWcYfy55pgAgU1LV9Q9XNrNv2sWG4W2XzE3ViV") + chainID := ids.FromStringOrPanic("2BMFrJ9xeh5JdwZEx6uuFcjfZC2SV2hdbMT8ee5HrvjtfJb5br") addressHex := "" - validationID := ids.FromStringOrPanic("225kHLzuaBd6rhxZ8aq91kmgLJyPTFtTFVAWJDaPyKRdDiTpQo") + validationID := ids.FromStringOrPanic("2Y3ZZZXxpzm46geqVuqFXeSFVbeKihgrfeXRDaiF4ds6R2N8M5") nonce := uint64(1) - weight := uint64(0) + weight := uint64(2) address, err := hex.DecodeString(addressHex) if err != nil { log.Fatalf("failed to decode address %q: %s\n", addressHex, err) } + skBytes, err := os.ReadFile("/Users/stephen/.avalanchego/staking/signer.key") + if err != nil { + log.Fatalf("failed to read signer key: %s\n", err) + } + + sk, err := bls.SecretKeyFromBytes(skBytes) + if err != nil { + log.Fatalf("failed to parse secret key: %s\n", err) + } + // MakeWallet fetches the available UTXOs owned by [kc] on the network that // [uri] is hosting and registers [subnetID]. walletSyncStartTime := time.Now() @@ -51,14 +65,19 @@ func main() { pWallet := wallet.P() context := pWallet.Builder().Context() - addressedCallPayload, err := message.NewSetSubnetValidatorWeight( + addressedCallPayload, err := message.NewSubnetValidatorWeight( validationID, nonce, weight, ) if err != nil { - log.Fatalf("failed to create SetSubnetValidatorWeight message: %s\n", err) + log.Fatalf("failed to create SubnetValidatorWeight message: %s\n", err) + } + addressedCallPayloadJSON, err := json.MarshalIndent(addressedCallPayload, "", "\t") + if err != nil { + log.Fatalf("failed to marshal SubnetValidatorWeight message: %s\n", err) } + log.Println(string(addressedCallPayloadJSON)) addressedCall, err := payload.NewAddressedCall( address, @@ -77,9 +96,20 @@ func main() { log.Fatalf("failed to create unsigned Warp message: %s\n", err) } + signers := set.NewBits() + signers.Add(0) // [signers] has weight from [vdr[0]] + + unsignedBytes := unsignedWarp.Bytes() + sig := bls.Sign(sk, unsignedBytes) + sigBytes := [bls.SignatureLen]byte{} + copy(sigBytes[:], bls.SignatureToBytes(sig)) + warp, err := warp.NewMessage( unsignedWarp, - &warp.BitSetSignature{}, + &warp.BitSetSignature{ + Signers: signers.Bytes(), + Signature: sigBytes, + }, ) if err != nil { log.Fatalf("failed to create Warp message: %s\n", err) diff --git a/wallet/subnet/primary/examples/sign-subnet-conversion/main.go b/wallet/subnet/primary/examples/sign-subnet-conversion/main.go new file mode 100644 index 00000000000..87f15985585 --- /dev/null +++ b/wallet/subnet/primary/examples/sign-subnet-conversion/main.go @@ -0,0 +1,119 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "log" + "net/netip" + "time" + + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/utils/compression" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + + p2pmessage "github.com/ava-labs/avalanchego/message" + warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" +) + +func main() { + uri := primary.LocalAPIURI + subnetID := ids.FromStringOrPanic("2DeHa7Qb6sufPkmQcFWG2uCd4pBPv9WB6dkzroiMQhd1NSRtof") + conversionID := ids.FromStringOrPanic("d84X4xQeXkAhnLQi2BqDyG6AEGbdJVGJCwx6a4c3UnBhD9vpZ") + infoClient := info.NewClient(uri) + networkID, err := infoClient.GetNetworkID(context.Background()) + if err != nil { + log.Fatalf("failed to fetch network ID: %s\n", err) + } + + subnetConversion, err := warpmessage.NewSubnetConversion(conversionID) + if err != nil { + log.Fatalf("failed to create SubnetConversion message: %s\n", err) + } + + addressedCall, err := payload.NewAddressedCall( + nil, + subnetConversion.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create AddressedCall message: %s\n", err) + } + + unsignedWarp, err := warp.NewUnsignedMessage( + networkID, + constants.PlatformChainID, + addressedCall.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create unsigned Warp message: %s\n", err) + } + + p, err := peer.StartTestPeer( + context.Background(), + netip.AddrPortFrom( + netip.AddrFrom4([4]byte{127, 0, 0, 1}), + 9651, + ), + networkID, + router.InboundHandlerFunc(func(_ context.Context, msg p2pmessage.InboundMessage) { + log.Printf("received %s: %s", msg.Op(), msg.Message()) + }), + ) + if err != nil { + log.Fatalf("failed to start peer: %s\n", err) + } + + mesageBuilder, err := p2pmessage.NewCreator( + logging.NoLog{}, + prometheus.NewRegistry(), + compression.TypeZstd, + time.Hour, + ) + if err != nil { + log.Fatalf("failed to create message builder: %s\n", err) + } + + appRequestPayload, err := proto.Marshal(&sdk.SignatureRequest{ + Message: unsignedWarp.Bytes(), + Justification: subnetID[:], + }) + if err != nil { + log.Fatalf("failed to marshal SignatureRequest: %s\n", err) + } + + appRequest, err := mesageBuilder.AppRequest( + constants.PlatformChainID, + 0, + time.Hour, + p2p.PrefixMessage( + p2p.ProtocolPrefix(p2p.SignatureRequestHandlerID), + appRequestPayload, + ), + ) + if err != nil { + log.Fatalf("failed to create AppRequest: %s\n", err) + } + + p.Send(context.Background(), appRequest) + + time.Sleep(5 * time.Second) + + p.StartClose() + err = p.AwaitClosed(context.Background()) + if err != nil { + log.Fatalf("failed to close peer: %s\n", err) + } +} diff --git a/wallet/subnet/primary/examples/sign-subnet-validator-registration/main.go b/wallet/subnet/primary/examples/sign-subnet-validator-registration/main.go index 8ba1bc80a09..6506d2477c2 100644 --- a/wallet/subnet/primary/examples/sign-subnet-validator-registration/main.go +++ b/wallet/subnet/primary/examples/sign-subnet-validator-registration/main.go @@ -14,14 +14,12 @@ import ( "google.golang.org/protobuf/proto" "github.com/ava-labs/avalanchego/api/info" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/network/p2p" "github.com/ava-labs/avalanchego/network/peer" "github.com/ava-labs/avalanchego/proto/pb/sdk" "github.com/ava-labs/avalanchego/snow/networking/router" "github.com/ava-labs/avalanchego/utils/compression" "github.com/ava-labs/avalanchego/utils/constants" - "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" @@ -33,59 +31,67 @@ import ( var registerSubnetValidatorJSON = []byte(`{ "subnetID": "2DeHa7Qb6sufPkmQcFWG2uCd4pBPv9WB6dkzroiMQhd1NSRtof", - "nodeID": "NodeID-9Nm8NNALws11M5J89rXgc46u6L52e7fmz", - "weight": 49463, + "nodeID": "0xb628ee3952a5de80fadd31ab030a67189edb1410", "blsPublicKey": [ + 143, + 167, + 255, + 128, + 221, + 92, + 126, + 190, + 134, + 189, + 157, + 166, + 6, + 55, + 92, + 125, + 223, + 231, + 71, + 85, + 122, + 110, + 110, + 49, + 215, + 14, + 1, + 226, + 146, + 140, + 73, + 75, + 113, + 163, 138, - 25, - 41, - 204, - 242, - 137, - 56, - 208, - 15, - 227, - 216, - 136, + 158, + 34, + 207, + 99, + 36, 137, - 144, - 59, - 120, - 205, - 181, - 230, - 232, - 225, + 55, + 191, + 28, + 186, 24, - 80, - 117, - 225, - 16, - 102, - 93, - 83, - 184, - 245, - 240, - 232, - 97, - 238, 49, - 217, - 4, - 131, - 121, - 19, - 213, - 202, - 233, - 38, - 19, - 70, - 58 + 199 ], - "expiry": 1727548306 + "expiry": 1727975059, + "remainingBalanceOwner": { + "threshold": 0, + "addresses": null + }, + "disableOwner": { + "threshold": 0, + "addresses": null + }, + "weight": 1 }`) func main() { @@ -106,7 +112,7 @@ func main() { log.Fatalf("failed to initialize RegisterSubnetValidator message: %s\n", err) } - var validationID ids.ID = hashing.ComputeHash256Array(registerSubnetValidator.Bytes()) + validationID := registerSubnetValidator.ValidationID() subnetValidatorRegistration, err := warpmessage.NewSubnetValidatorRegistration( validationID, true, diff --git a/wallet/subnet/primary/examples/sign-subnet-validator-removal/main.go b/wallet/subnet/primary/examples/sign-subnet-validator-removal/main.go new file mode 100644 index 00000000000..02ae39abef7 --- /dev/null +++ b/wallet/subnet/primary/examples/sign-subnet-validator-removal/main.go @@ -0,0 +1,196 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "encoding/json" + "log" + "net/netip" + "time" + + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/utils/compression" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + + p2pmessage "github.com/ava-labs/avalanchego/message" + warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" +) + +var registerSubnetValidatorJSON = []byte(`{ + "subnetID": "2DeHa7Qb6sufPkmQcFWG2uCd4pBPv9WB6dkzroiMQhd1NSRtof", + "nodeID": "0xb628ee3952a5de80fadd31ab030a67189edb1410", + "blsPublicKey": [ + 143, + 167, + 255, + 128, + 221, + 92, + 126, + 190, + 134, + 189, + 157, + 166, + 6, + 55, + 92, + 125, + 223, + 231, + 71, + 85, + 122, + 110, + 110, + 49, + 215, + 14, + 1, + 226, + 146, + 140, + 73, + 75, + 113, + 163, + 138, + 158, + 34, + 207, + 99, + 36, + 137, + 55, + 191, + 28, + 186, + 24, + 49, + 199 + ], + "expiry": 1727975059, + "remainingBalanceOwner": { + "threshold": 0, + "addresses": null + }, + "disableOwner": { + "threshold": 0, + "addresses": null + }, + "weight": 1 +}`) + +func main() { + uri := primary.LocalAPIURI + infoClient := info.NewClient(uri) + networkID, err := infoClient.GetNetworkID(context.Background()) + if err != nil { + log.Fatalf("failed to fetch network ID: %s\n", err) + } + + var registerSubnetValidator warpmessage.RegisterSubnetValidator + err = json.Unmarshal(registerSubnetValidatorJSON, ®isterSubnetValidator) + if err != nil { + log.Fatalf("failed to unmarshal RegisterSubnetValidator message: %s\n", err) + } + err = warpmessage.Initialize(®isterSubnetValidator) + if err != nil { + log.Fatalf("failed to initialize RegisterSubnetValidator message: %s\n", err) + } + + validationID := registerSubnetValidator.ValidationID() + subnetValidatorRegistration, err := warpmessage.NewSubnetValidatorRegistration( + validationID, + false, + ) + if err != nil { + log.Fatalf("failed to create SubnetValidatorRegistration message: %s\n", err) + } + + addressedCall, err := payload.NewAddressedCall( + nil, + subnetValidatorRegistration.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create AddressedCall message: %s\n", err) + } + + unsignedWarp, err := warp.NewUnsignedMessage( + networkID, + constants.PlatformChainID, + addressedCall.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create unsigned Warp message: %s\n", err) + } + + p, err := peer.StartTestPeer( + context.Background(), + netip.AddrPortFrom( + netip.AddrFrom4([4]byte{127, 0, 0, 1}), + 9651, + ), + networkID, + router.InboundHandlerFunc(func(_ context.Context, msg p2pmessage.InboundMessage) { + log.Printf("received %s: %s", msg.Op(), msg.Message()) + }), + ) + if err != nil { + log.Fatalf("failed to start peer: %s\n", err) + } + + mesageBuilder, err := p2pmessage.NewCreator( + logging.NoLog{}, + prometheus.NewRegistry(), + compression.TypeZstd, + time.Hour, + ) + if err != nil { + log.Fatalf("failed to create message builder: %s\n", err) + } + + appRequestPayload, err := proto.Marshal(&sdk.SignatureRequest{ + Message: unsignedWarp.Bytes(), + Justification: registerSubnetValidator.Bytes(), + }) + if err != nil { + log.Fatalf("failed to marshal SignatureRequest: %s\n", err) + } + + appRequest, err := mesageBuilder.AppRequest( + constants.PlatformChainID, + 0, + time.Hour, + p2p.PrefixMessage( + p2p.ProtocolPrefix(p2p.SignatureRequestHandlerID), + appRequestPayload, + ), + ) + if err != nil { + log.Fatalf("failed to create AppRequest: %s\n", err) + } + + p.Send(context.Background(), appRequest) + + time.Sleep(5 * time.Second) + + p.StartClose() + err = p.AwaitClosed(context.Background()) + if err != nil { + log.Fatalf("failed to close peer: %s\n", err) + } +} diff --git a/wallet/subnet/primary/examples/sign-subnet-validator-weight/main.go b/wallet/subnet/primary/examples/sign-subnet-validator-weight/main.go new file mode 100644 index 00000000000..26a8dd1cfcb --- /dev/null +++ b/wallet/subnet/primary/examples/sign-subnet-validator-weight/main.go @@ -0,0 +1,127 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "encoding/json" + "log" + "net/netip" + "time" + + "github.com/prometheus/client_golang/prometheus" + "google.golang.org/protobuf/proto" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/network/p2p" + "github.com/ava-labs/avalanchego/network/peer" + "github.com/ava-labs/avalanchego/proto/pb/sdk" + "github.com/ava-labs/avalanchego/snow/networking/router" + "github.com/ava-labs/avalanchego/utils/compression" + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/payload" + "github.com/ava-labs/avalanchego/wallet/subnet/primary" + + p2pmessage "github.com/ava-labs/avalanchego/message" + warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" +) + +var subnetValidatorWeightJSON = []byte(`{ + "validationID": "2Y3ZZZXxpzm46geqVuqFXeSFVbeKihgrfeXRDaiF4ds6R2N8M5", + "nonce": 1, + "weight": 2 +}`) + +func main() { + uri := primary.LocalAPIURI + infoClient := info.NewClient(uri) + networkID, err := infoClient.GetNetworkID(context.Background()) + if err != nil { + log.Fatalf("failed to fetch network ID: %s\n", err) + } + + var subnetValidatorWeight warpmessage.SubnetValidatorWeight + err = json.Unmarshal(subnetValidatorWeightJSON, &subnetValidatorWeight) + if err != nil { + log.Fatalf("failed to unmarshal SubnetValidatorWeight message: %s\n", err) + } + err = warpmessage.Initialize(&subnetValidatorWeight) + if err != nil { + log.Fatalf("failed to initialize SubnetValidatorWeight message: %s\n", err) + } + + addressedCall, err := payload.NewAddressedCall( + nil, + subnetValidatorWeight.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create AddressedCall message: %s\n", err) + } + + unsignedWarp, err := warp.NewUnsignedMessage( + networkID, + constants.PlatformChainID, + addressedCall.Bytes(), + ) + if err != nil { + log.Fatalf("failed to create unsigned Warp message: %s\n", err) + } + + p, err := peer.StartTestPeer( + context.Background(), + netip.AddrPortFrom( + netip.AddrFrom4([4]byte{127, 0, 0, 1}), + 9651, + ), + networkID, + router.InboundHandlerFunc(func(_ context.Context, msg p2pmessage.InboundMessage) { + log.Printf("received %s: %s", msg.Op(), msg.Message()) + }), + ) + if err != nil { + log.Fatalf("failed to start peer: %s\n", err) + } + + mesageBuilder, err := p2pmessage.NewCreator( + logging.NoLog{}, + prometheus.NewRegistry(), + compression.TypeZstd, + time.Hour, + ) + if err != nil { + log.Fatalf("failed to create message builder: %s\n", err) + } + + appRequestPayload, err := proto.Marshal(&sdk.SignatureRequest{ + Message: unsignedWarp.Bytes(), + }) + if err != nil { + log.Fatalf("failed to marshal SignatureRequest: %s\n", err) + } + + appRequest, err := mesageBuilder.AppRequest( + constants.PlatformChainID, + 0, + time.Hour, + p2p.PrefixMessage( + p2p.ProtocolPrefix(p2p.SignatureRequestHandlerID), + appRequestPayload, + ), + ) + if err != nil { + log.Fatalf("failed to create AppRequest: %s\n", err) + } + + p.Send(context.Background(), appRequest) + + time.Sleep(5 * time.Second) + + p.StartClose() + err = p.AwaitClosed(context.Background()) + if err != nil { + log.Fatalf("failed to close peer: %s\n", err) + } +} diff --git a/wallet/subnet/primary/wallet.go b/wallet/subnet/primary/wallet.go index 751cd03a304..cb0cab2be56 100644 --- a/wallet/subnet/primary/wallet.go +++ b/wallet/subnet/primary/wallet.go @@ -10,6 +10,7 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/keychain" "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/chain/c" "github.com/ava-labs/avalanchego/wallet/chain/p" "github.com/ava-labs/avalanchego/wallet/chain/x" @@ -108,7 +109,10 @@ func MakeWallet(ctx context.Context, config *WalletConfig) (Wallet, error) { if err != nil { return nil, err } - // TODO: Fetch validation disableOwners + for _, validationID := range config.ValidationIDs { + // TODO: Fetch validation disableOwners + subnetOwners[validationID] = &secp256k1fx.OutputOwners{} + } pUTXOs := common.NewChainUTXOs(constants.PlatformChainID, avaxState.UTXOs) pBackend := pwallet.NewBackend(avaxState.PCTX, pUTXOs, subnetOwners)