From f4b4142ff1c0ede4852f9a76a232586c1844b8c2 Mon Sep 17 00:00:00 2001 From: Stephen Buttolph Date: Wed, 13 Nov 2024 10:13:12 -0500 Subject: [PATCH] Add SoV deactivation owner support to the P-chain wallet (#3541) --- vms/platformvm/block/builder/helpers_test.go | 1 + vms/platformvm/block/executor/helpers_test.go | 1 + .../block/executor/verifier_test.go | 3 ++ vms/platformvm/client.go | 20 +++++++++- vms/platformvm/txs/executor/helpers_test.go | 1 + .../txs/executor/standard_tx_executor_test.go | 17 ++++++--- vms/platformvm/txs/txstest/wallet.go | 16 +++++++- vms/platformvm/vm_test.go | 1 + wallet/chain/p/builder/builder.go | 22 +++++------ wallet/chain/p/signer/signer.go | 2 +- wallet/chain/p/signer/visitor.go | 30 +++++++-------- wallet/chain/p/wallet/backend.go | 28 +++++++------- wallet/chain/p/wallet/backend_visitor.go | 37 ++++++++++++++++++- wallet/subnet/primary/wallet.go | 23 ++++++++++-- 14 files changed, 148 insertions(+), 54 deletions(-) diff --git a/vms/platformvm/block/builder/helpers_test.go b/vms/platformvm/block/builder/helpers_test.go index 2d20ce56dc1a..194116273a0c 100644 --- a/vms/platformvm/block/builder/helpers_test.go +++ b/vms/platformvm/block/builder/helpers_test.go @@ -218,6 +218,7 @@ func newWallet(t testing.TB, e *environment, c walletConfig) wallet.Wallet { e.state, secp256k1fx.NewKeychain(c.keys...), c.subnetIDs, + nil, // validationIDs []ids.ID{e.ctx.CChainID, e.ctx.XChainID}, ) } diff --git a/vms/platformvm/block/executor/helpers_test.go b/vms/platformvm/block/executor/helpers_test.go index 14554e248b5d..cef542533ded 100644 --- a/vms/platformvm/block/executor/helpers_test.go +++ b/vms/platformvm/block/executor/helpers_test.go @@ -224,6 +224,7 @@ func newWallet(t testing.TB, e *environment, c walletConfig) wallet.Wallet { e.state, secp256k1fx.NewKeychain(c.keys...), c.subnetIDs, + nil, // validationIDs []ids.ID{e.ctx.CChainID, e.ctx.XChainID}, ) } diff --git a/vms/platformvm/block/executor/verifier_test.go b/vms/platformvm/block/executor/verifier_test.go index 16b129b36882..f1715842aedf 100644 --- a/vms/platformvm/block/executor/verifier_test.go +++ b/vms/platformvm/block/executor/verifier_test.go @@ -197,6 +197,7 @@ func TestVerifierVisitAtomicBlock(t *testing.T) { verifier.state, secp256k1fx.NewKeychain(genesis.EWOQKey), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) exportedOutput = &avax.TransferableOutput{ @@ -346,6 +347,7 @@ func TestVerifierVisitStandardBlock(t *testing.T) { verifier.state, secp256k1fx.NewKeychain(genesis.EWOQKey), nil, // subnetIDs + nil, // validationIDs []ids.ID{ctx.XChainID}, // Read the UTXO to import ) initialTimestamp = verifier.state.GetTimestamp() @@ -1098,6 +1100,7 @@ func TestBlockExecutionWithComplexity(t *testing.T) { verifier.state, secp256k1fx.NewKeychain(genesis.EWOQKey), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) diff --git a/vms/platformvm/client.go b/vms/platformvm/client.go index 3a3469fff626..f795594f6a00 100644 --- a/vms/platformvm/client.go +++ b/vms/platformvm/client.go @@ -646,7 +646,7 @@ func GetSubnetOwners( ctx context.Context, subnetIDs ...ids.ID, ) (map[ids.ID]fx.Owner, error) { - subnetOwners := map[ids.ID]fx.Owner{} + subnetOwners := make(map[ids.ID]fx.Owner, len(subnetIDs)) for _, subnetID := range subnetIDs { subnetInfo, err := c.GetSubnet(ctx, subnetID) if err != nil { @@ -660,3 +660,21 @@ func GetSubnetOwners( } return subnetOwners, nil } + +// GetDeactivationOwners returns a map of validation ID to subnet-only +// validation deactivation owner +func GetDeactivationOwners( + c Client, + ctx context.Context, + validationIDs ...ids.ID, +) (map[ids.ID]fx.Owner, error) { + deactivationOwners := make(map[ids.ID]fx.Owner, len(validationIDs)) + for _, validationID := range validationIDs { + sov, _, err := c.GetSubnetOnlyValidator(ctx, validationID) + if err != nil { + return nil, err + } + deactivationOwners[validationID] = sov.DeactivationOwner + } + return deactivationOwners, nil +} diff --git a/vms/platformvm/txs/executor/helpers_test.go b/vms/platformvm/txs/executor/helpers_test.go index 3823355795d7..7c1dbdc1e005 100644 --- a/vms/platformvm/txs/executor/helpers_test.go +++ b/vms/platformvm/txs/executor/helpers_test.go @@ -192,6 +192,7 @@ func newWallet(t testing.TB, e *environment, c walletConfig) wallet.Wallet { e.state, secp256k1fx.NewKeychain(c.keys...), c.subnetIDs, + nil, // validationIDs c.chainIDs, ) } diff --git a/vms/platformvm/txs/executor/standard_tx_executor_test.go b/vms/platformvm/txs/executor/standard_tx_executor_test.go index 6ce54be5e738..8682c3b06e2f 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor_test.go +++ b/vms/platformvm/txs/executor/standard_tx_executor_test.go @@ -2383,6 +2383,7 @@ func TestStandardExecutorConvertSubnetTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) flowChecker = utxo.NewVerifier( @@ -2562,6 +2563,7 @@ func TestStandardExecutorConvertSubnetTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), []ids.ID{subnetID}, + nil, // validationIDs nil, // chainIDs ) chainID = ids.GenerateTestID() @@ -2707,6 +2709,7 @@ func TestStandardExecutorRegisterSubnetValidatorTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) flowChecker = utxo.NewVerifier( @@ -3122,23 +3125,23 @@ func TestStandardExecutorRegisterSubnetValidatorTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) - message := test.message - if message == nil { - message = warpMessage.Bytes() - } registerSubnetValidatorTx, err := wallet.IssueRegisterSubnetValidatorTx( test.balance, pop.ProofOfPossession, - message, + warpMessage.Bytes(), test.builderOptions..., ) require.NoError(err) + unsignedTx := registerSubnetValidatorTx.Unsigned.(*txs.RegisterSubnetValidatorTx) + if test.message != nil { + unsignedTx.Message = test.message + } if test.updateTx != nil { - unsignedTx := registerSubnetValidatorTx.Unsigned.(*txs.RegisterSubnetValidatorTx) test.updateTx(unsignedTx) } @@ -3239,6 +3242,7 @@ func TestStandardExecutorSetSubnetValidatorWeightTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) flowChecker = utxo.NewVerifier( @@ -3634,6 +3638,7 @@ func TestStandardExecutorSetSubnetValidatorWeightTx(t *testing.T) { baseState, secp256k1fx.NewKeychain(genesistest.DefaultFundedKeys...), nil, // subnetIDs + nil, // validationIDs nil, // chainIDs ) diff --git a/vms/platformvm/txs/txstest/wallet.go b/vms/platformvm/txs/txstest/wallet.go index f26f2d612aef..2de042d402ef 100644 --- a/vms/platformvm/txs/txstest/wallet.go +++ b/vms/platformvm/txs/txstest/wallet.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/fx" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" "github.com/ava-labs/avalanchego/vms/secp256k1fx" "github.com/ava-labs/avalanchego/wallet/chain/p/builder" "github.com/ava-labs/avalanchego/wallet/chain/p/signer" @@ -32,6 +33,7 @@ func NewWallet( state state.State, kc *secp256k1fx.Keychain, subnetIDs []ids.ID, + validationIDs []ids.ID, chainIDs []ids.ID, ) wallet.Wallet { var ( @@ -74,12 +76,24 @@ func NewWallet( } } - owners := make(map[ids.ID]fx.Owner, len(subnetIDs)) + owners := make(map[ids.ID]fx.Owner, len(subnetIDs)+len(validationIDs)) for _, subnetID := range subnetIDs { owner, err := state.GetSubnetOwner(subnetID) require.NoError(err) owners[subnetID] = owner } + for _, validationID := range validationIDs { + sov, err := state.GetSubnetOnlyValidator(validationID) + require.NoError(err) + + var owner message.PChainOwner + _, err = txs.Codec.Unmarshal(sov.DeactivationOwner, &owner) + require.NoError(err) + owners[validationID] = &secp256k1fx.OutputOwners{ + Threshold: owner.Threshold, + Addrs: owner.Addresses, + } + } builderContext := newContext(ctx, config, state) backend := wallet.NewBackend( diff --git a/vms/platformvm/vm_test.go b/vms/platformvm/vm_test.go index e8c83ba3350e..3a9f2f695817 100644 --- a/vms/platformvm/vm_test.go +++ b/vms/platformvm/vm_test.go @@ -241,6 +241,7 @@ func newWallet(t testing.TB, vm *VM, c walletConfig) wallet.Wallet { vm.state, secp256k1fx.NewKeychain(c.keys...), c.subnetIDs, + nil, // validationIDs []ids.ID{vm.ctx.CChainID, vm.ctx.XChainID}, ) } diff --git a/wallet/chain/p/builder/builder.go b/wallet/chain/p/builder/builder.go index 8ca24ba8f7d9..cb58b2dc2485 100644 --- a/wallet/chain/p/builder/builder.go +++ b/wallet/chain/p/builder/builder.go @@ -301,7 +301,7 @@ type Builder interface { type Backend interface { UTXOs(ctx context.Context, sourceChainID ids.ID) ([]*avax.UTXO, error) - GetSubnetOwner(ctx context.Context, subnetID ids.ID) (fx.Owner, error) + GetOwner(ctx context.Context, ownerID ids.ID) (fx.Owner, error) } type builder struct { @@ -458,7 +458,7 @@ func (b *builder) NewAddSubnetValidatorTx( toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) - subnetAuth, err := b.authorizeSubnet(vdr.Subnet, ops) + subnetAuth, err := b.authorize(vdr.Subnet, ops) if err != nil { return nil, err } @@ -516,7 +516,7 @@ func (b *builder) NewRemoveSubnetValidatorTx( toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) - subnetAuth, err := b.authorizeSubnet(subnetID, ops) + subnetAuth, err := b.authorize(subnetID, ops) if err != nil { return nil, err } @@ -619,7 +619,7 @@ func (b *builder) NewCreateChainTx( toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) - subnetAuth, err := b.authorizeSubnet(subnetID, ops) + subnetAuth, err := b.authorize(subnetID, ops) if err != nil { return nil, err } @@ -750,7 +750,7 @@ func (b *builder) NewTransferSubnetOwnershipTx( toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) - subnetAuth, err := b.authorizeSubnet(subnetID, ops) + subnetAuth, err := b.authorize(subnetID, ops) if err != nil { return nil, err } @@ -827,7 +827,7 @@ func (b *builder) NewConvertSubnetTx( toStake = map[ids.ID]uint64{} ops = common.NewOptions(options) ) - subnetAuth, err := b.authorizeSubnet(subnetID, ops) + subnetAuth, err := b.authorize(subnetID, ops) if err != nil { return nil, err } @@ -1215,7 +1215,7 @@ func (b *builder) NewTransformSubnetTx( toStake := map[ids.ID]uint64{} ops := common.NewOptions(options) - subnetAuth, err := b.authorizeSubnet(subnetID, ops) + subnetAuth, err := b.authorize(subnetID, ops) if err != nil { return nil, err } @@ -1754,12 +1754,12 @@ func (b *builder) spend( return s.inputs, s.changeOutputs, s.stakeOutputs, nil } -func (b *builder) authorizeSubnet(subnetID ids.ID, options *common.Options) (*secp256k1fx.Input, error) { - ownerIntf, err := b.backend.GetSubnetOwner(options.Context(), subnetID) +func (b *builder) authorize(ownerID ids.ID, options *common.Options) (*secp256k1fx.Input, error) { + ownerIntf, err := b.backend.GetOwner(options.Context(), ownerID) if err != nil { return nil, fmt.Errorf( - "failed to fetch subnet owner for %q: %w", - subnetID, + "failed to fetch owner for %q: %w", + ownerID, err, ) } diff --git a/wallet/chain/p/signer/signer.go b/wallet/chain/p/signer/signer.go index 08b3a9d9963b..d0c8c0dd07fa 100644 --- a/wallet/chain/p/signer/signer.go +++ b/wallet/chain/p/signer/signer.go @@ -29,7 +29,7 @@ type Signer interface { type Backend interface { GetUTXO(ctx stdcontext.Context, chainID, utxoID ids.ID) (*avax.UTXO, error) - GetSubnetOwner(ctx stdcontext.Context, subnetID ids.ID) (fx.Owner, error) + GetOwner(ctx stdcontext.Context, ownerID ids.ID) (fx.Owner, error) } type txSigner struct { diff --git a/wallet/chain/p/signer/visitor.go b/wallet/chain/p/signer/visitor.go index c595ab82a30a..a155b351faa3 100644 --- a/wallet/chain/p/signer/visitor.go +++ b/wallet/chain/p/signer/visitor.go @@ -28,7 +28,7 @@ var ( ErrUnknownInputType = errors.New("unknown input type") ErrUnknownOutputType = errors.New("unknown output type") ErrInvalidUTXOSigIndex = errors.New("invalid UTXO signature index") - ErrUnknownSubnetAuthType = errors.New("unknown subnet auth type") + ErrUnknownAuthType = errors.New("unknown auth type") ErrUnknownOwnerType = errors.New("unknown owner type") ErrUnknownCredentialType = errors.New("unknown credential type") @@ -64,7 +64,7 @@ func (s *visitor) AddSubnetValidatorTx(tx *txs.AddSubnetValidatorTx) error { if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.SubnetValidator.Subnet, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.SubnetValidator.Subnet, tx.SubnetAuth) if err != nil { return err } @@ -85,7 +85,7 @@ func (s *visitor) CreateChainTx(tx *txs.CreateChainTx) error { if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.SubnetID, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.SubnetID, tx.SubnetAuth) if err != nil { return err } @@ -127,7 +127,7 @@ func (s *visitor) RemoveSubnetValidatorTx(tx *txs.RemoveSubnetValidatorTx) error if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.Subnet, tx.SubnetAuth) if err != nil { return err } @@ -140,7 +140,7 @@ func (s *visitor) TransformSubnetTx(tx *txs.TransformSubnetTx) error { if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.Subnet, tx.SubnetAuth) if err != nil { return err } @@ -169,7 +169,7 @@ func (s *visitor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) e if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.Subnet, tx.SubnetAuth) if err != nil { return err } @@ -190,7 +190,7 @@ func (s *visitor) ConvertSubnetTx(tx *txs.ConvertSubnetTx) error { if err != nil { return err } - subnetAuthSigners, err := s.getSubnetSigners(tx.Subnet, tx.SubnetAuth) + subnetAuthSigners, err := s.getAuthSigners(tx.Subnet, tx.SubnetAuth) if err != nil { return err } @@ -269,17 +269,17 @@ func (s *visitor) getSigners(sourceChainID ids.ID, ins []*avax.TransferableInput return txSigners, nil } -func (s *visitor) getSubnetSigners(subnetID ids.ID, subnetAuth verify.Verifiable) ([]keychain.Signer, error) { - subnetInput, ok := subnetAuth.(*secp256k1fx.Input) +func (s *visitor) getAuthSigners(ownerID ids.ID, auth verify.Verifiable) ([]keychain.Signer, error) { + input, ok := auth.(*secp256k1fx.Input) if !ok { - return nil, ErrUnknownSubnetAuthType + return nil, ErrUnknownAuthType } - ownerIntf, err := s.backend.GetSubnetOwner(s.ctx, subnetID) + ownerIntf, err := s.backend.GetOwner(s.ctx, ownerID) if err != nil { return nil, fmt.Errorf( - "failed to fetch subnet owner for %q: %w", - subnetID, + "failed to fetch owner for %q: %w", + ownerID, err, ) } @@ -288,8 +288,8 @@ func (s *visitor) getSubnetSigners(subnetID ids.ID, subnetAuth verify.Verifiable return nil, ErrUnknownOwnerType } - authSigners := make([]keychain.Signer, len(subnetInput.SigIndices)) - for sigIndex, addrIndex := range subnetInput.SigIndices { + authSigners := make([]keychain.Signer, len(input.SigIndices)) + for sigIndex, addrIndex := range input.SigIndices { if addrIndex >= uint32(len(owner.Addrs)) { return nil, ErrInvalidUTXOSigIndex } diff --git a/wallet/chain/p/wallet/backend.go b/wallet/chain/p/wallet/backend.go index f46902fa8a51..da6b31083f1a 100644 --- a/wallet/chain/p/wallet/backend.go +++ b/wallet/chain/p/wallet/backend.go @@ -34,15 +34,15 @@ type backend struct { context *builder.Context - subnetOwnerLock sync.RWMutex - subnetOwner map[ids.ID]fx.Owner // subnetID -> owner + ownersLock sync.RWMutex + owners map[ids.ID]fx.Owner // subnetID or validationID -> owner } -func NewBackend(context *builder.Context, utxos common.ChainUTXOs, subnetOwner map[ids.ID]fx.Owner) Backend { +func NewBackend(context *builder.Context, utxos common.ChainUTXOs, owners map[ids.ID]fx.Owner) Backend { return &backend{ - ChainUTXOs: utxos, - context: context, - subnetOwner: subnetOwner, + ChainUTXOs: utxos, + context: context, + owners: owners, } } @@ -79,20 +79,20 @@ func (b *backend) removeUTXOs(ctx context.Context, sourceChain ids.ID, utxoIDs s return nil } -func (b *backend) GetSubnetOwner(_ context.Context, subnetID ids.ID) (fx.Owner, error) { - b.subnetOwnerLock.RLock() - defer b.subnetOwnerLock.RUnlock() +func (b *backend) GetOwner(_ context.Context, ownerID ids.ID) (fx.Owner, error) { + b.ownersLock.RLock() + defer b.ownersLock.RUnlock() - owner, exists := b.subnetOwner[subnetID] + owner, exists := b.owners[ownerID] if !exists { return nil, database.ErrNotFound } return owner, nil } -func (b *backend) setSubnetOwner(subnetID ids.ID, owner fx.Owner) { - b.subnetOwnerLock.Lock() - defer b.subnetOwnerLock.Unlock() +func (b *backend) setOwner(ownerID ids.ID, owner fx.Owner) { + b.ownersLock.Lock() + defer b.ownersLock.Unlock() - b.subnetOwner[subnetID] = owner + b.owners[ownerID] = owner } diff --git a/wallet/chain/p/wallet/backend_visitor.go b/wallet/chain/p/wallet/backend_visitor.go index fdd3c1a0e40a..8df2ba7962bf 100644 --- a/wallet/chain/p/wallet/backend_visitor.go +++ b/wallet/chain/p/wallet/backend_visitor.go @@ -11,6 +11,10 @@ import ( "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "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" + "github.com/ava-labs/avalanchego/vms/secp256k1fx" ) var ( @@ -51,7 +55,7 @@ func (b *backendVisitor) CreateChainTx(tx *txs.CreateChainTx) error { } func (b *backendVisitor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { - b.b.setSubnetOwner( + b.b.setOwner( b.txID, tx.Owner, ) @@ -108,7 +112,7 @@ func (b *backendVisitor) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessD } func (b *backendVisitor) TransferSubnetOwnershipTx(tx *txs.TransferSubnetOwnershipTx) error { - b.b.setSubnetOwner( + b.b.setOwner( tx.Subnet, tx.Owner, ) @@ -120,10 +124,39 @@ func (b *backendVisitor) BaseTx(tx *txs.BaseTx) error { } func (b *backendVisitor) ConvertSubnetTx(tx *txs.ConvertSubnetTx) error { + for i, vdr := range tx.Validators { + b.b.setOwner( + tx.Subnet.Append(uint32(i)), + &secp256k1fx.OutputOwners{ + Threshold: vdr.DeactivationOwner.Threshold, + Addrs: vdr.DeactivationOwner.Addresses, + }, + ) + } return b.baseTx(&tx.BaseTx) } func (b *backendVisitor) RegisterSubnetValidatorTx(tx *txs.RegisterSubnetValidatorTx) error { + warpMessage, err := warp.ParseMessage(tx.Message) + if err != nil { + return err + } + addressedCallPayload, err := payload.ParseAddressedCall(warpMessage.Payload) + if err != nil { + return err + } + registerSubnetValidatorMessage, err := message.ParseRegisterSubnetValidator(addressedCallPayload.Payload) + if err != nil { + return err + } + + b.b.setOwner( + registerSubnetValidatorMessage.ValidationID(), + &secp256k1fx.OutputOwners{ + Threshold: registerSubnetValidatorMessage.DisableOwner.Threshold, + Addrs: registerSubnetValidatorMessage.DisableOwner.Addresses, + }, + ) return b.baseTx(&tx.BaseTx) } diff --git a/wallet/subnet/primary/wallet.go b/wallet/subnet/primary/wallet.go index 2b35c944c39c..55dc72f06146 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/platformvm/fx" "github.com/ava-labs/avalanchego/wallet/chain/c" "github.com/ava-labs/avalanchego/wallet/chain/p" "github.com/ava-labs/avalanchego/wallet/chain/x" @@ -73,9 +74,12 @@ type WalletConfig struct { // Keys to use for signing all transactions. AVAXKeychain keychain.Keychain // required EthKeychain c.EthKeychain // required - // Subnet IDs that the wallet should know about to be able to - // generate transactions. + // Subnet IDs that the wallet should know about to be able to generate + // transactions. SubnetIDs []ids.ID // optional + // Validation IDs that the wallet should know about to be able to generate + // transactions. + ValidationIDs []ids.ID // optional } // MakeWallet returns a wallet that supports issuing transactions to the chains @@ -105,8 +109,21 @@ func MakeWallet(ctx context.Context, config *WalletConfig) (Wallet, error) { if err != nil { return nil, err } + deactivationOwners, err := platformvm.GetDeactivationOwners(avaxState.PClient, ctx, config.ValidationIDs...) + if err != nil { + return nil, err + } + + owners := make(map[ids.ID]fx.Owner, len(subnetOwners)+len(deactivationOwners)) + for id, owner := range subnetOwners { + owners[id] = owner + } + for id, owner := range deactivationOwners { + owners[id] = owner + } + pUTXOs := common.NewChainUTXOs(constants.PlatformChainID, avaxState.UTXOs) - pBackend := pwallet.NewBackend(avaxState.PCTX, pUTXOs, subnetOwners) + pBackend := pwallet.NewBackend(avaxState.PCTX, pUTXOs, owners) pClient := p.NewClient(avaxState.PClient, pBackend) pBuilder := pbuilder.New(avaxAddrs, avaxState.PCTX, pBackend) pSigner := psigner.New(config.AVAXKeychain, pBackend)