From 54d2188552ea67f7fcd4b021eb497b0e4f9ac229 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Jun 2026 13:51:54 -0400 Subject: [PATCH 1/2] refactor: replace func-typed injection seams in pkg/pchain with interfaces The issue*Tx helpers took the wallet call as a function-typed parameter so unit tests could stub it. With multi-parameter builders the inline function types grew longer than the helper bodies (and the pattern was recently copied into an even larger 10-parameter signature in #28). Replace each seam with a single-method issuer interface mirroring the corresponding avalanchego wallet/chain/p method, so w.PWallet() satisfies them directly and tests stub a small struct instead of a closure. No behavior change: exported API, error messages, and wrapping are identical. Also gofmt's five files that were unformatted on main (same whitespace fixes as #29). --- cmd/subnet.go | 22 +- pkg/pchain/pchain.go | 128 +++++---- pkg/pchain/pchain_integration_test.go | 6 +- pkg/pchain/pchain_test.go | 372 +++++++++++++++----------- pkg/wallet/keychain.go | 1 - pkg/wallet/ledger.go | 6 +- pkg/wallet/wallet.go | 22 +- 7 files changed, 332 insertions(+), 225 deletions(-) diff --git a/cmd/subnet.go b/cmd/subnet.go index 1ac2c6c..f056d2c 100644 --- a/cmd/subnet.go +++ b/cmd/subnet.go @@ -23,17 +23,17 @@ import ( const defaultValidatorWeight uint64 = 100 var ( - subnetID string - subnetNewOwner string - subnetChainID string - subnetManager string - subnetValidatorIPs string - subnetValidatorIDs string - subnetValidatorBLS string - subnetValidatorPoP string - subnetValBalance float64 - subnetMockVal bool - subnetValidatorWeights string + subnetID string + subnetNewOwner string + subnetChainID string + subnetManager string + subnetValidatorIPs string + subnetValidatorIDs string + subnetValidatorBLS string + subnetValidatorPoP string + subnetValBalance float64 + subnetMockVal bool + subnetValidatorWeights string ) var subnetCmd = &cobra.Command{ diff --git a/pkg/pchain/pchain.go b/pkg/pchain/pchain.go index 9e333a3..3f40f21 100644 --- a/pkg/pchain/pchain.go +++ b/pkg/pchain/pchain.go @@ -16,6 +16,60 @@ import ( "github.com/ava-labs/platform-cli/pkg/wallet" ) +// ============================================================================= +// Issuer Seams +// ============================================================================= +// +// Each unexported issue*Tx helper depends on a small, single-method interface +// rather than the full avalanchego wallet. The avalanchego P-Chain wallet +// (pwallet.Wallet, returned by w.PWallet()) satisfies all of them, and tests +// supply small stub implementations. + +// baseTxIssuer issues a P-Chain BaseTx. +type baseTxIssuer interface { + IssueBaseTx(outputs []*avax.TransferableOutput, options ...common.Option) (*txs.Tx, error) +} + +// exportTxIssuer issues a P-Chain ExportTx. +type exportTxIssuer interface { + IssueExportTx(chainID ids.ID, outputs []*avax.TransferableOutput, options ...common.Option) (*txs.Tx, error) +} + +// importTxIssuer issues a P-Chain ImportTx. +type importTxIssuer interface { + IssueImportTx(chainID ids.ID, to *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error) +} + +// permissionlessValidatorTxIssuer issues an AddPermissionlessValidatorTx. +type permissionlessValidatorTxIssuer interface { + IssueAddPermissionlessValidatorTx(vdr *txs.SubnetValidator, signer signer.Signer, assetID ids.ID, validationRewardsOwner *secp256k1fx.OutputOwners, delegationRewardsOwner *secp256k1fx.OutputOwners, shares uint32, options ...common.Option) (*txs.Tx, error) +} + +// permissionlessDelegatorTxIssuer issues an AddPermissionlessDelegatorTx. +type permissionlessDelegatorTxIssuer interface { + IssueAddPermissionlessDelegatorTx(vdr *txs.SubnetValidator, assetID ids.ID, rewardsOwner *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error) +} + +// createSubnetTxIssuer issues a CreateSubnetTx. +type createSubnetTxIssuer interface { + IssueCreateSubnetTx(owner *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error) +} + +// transferSubnetOwnershipTxIssuer issues a TransferSubnetOwnershipTx. +type transferSubnetOwnershipTxIssuer interface { + IssueTransferSubnetOwnershipTx(subnetID ids.ID, owner *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error) +} + +// convertSubnetToL1TxIssuer issues a ConvertSubnetToL1Tx. +type convertSubnetToL1TxIssuer interface { + IssueConvertSubnetToL1Tx(subnetID ids.ID, chainID ids.ID, address []byte, validators []*txs.ConvertSubnetToL1Validator, options ...common.Option) (*txs.Tx, error) +} + +// createChainTxIssuer issues a CreateChainTx. +type createChainTxIssuer interface { + IssueCreateChainTx(subnetID ids.ID, genesis []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, options ...common.Option) (*txs.Tx, error) +} + // ============================================================================= // Transfers // ============================================================================= @@ -23,28 +77,28 @@ import ( // Send sends AVAX on the P-Chain (IssueBaseTx). func Send(ctx context.Context, w *wallet.Wallet, to ids.ShortID, amountNAVAX uint64) (ids.ID, error) { avaxAssetID := w.PWallet().Builder().Context().AVAXAssetID - return issueSendTx(w.PWallet().IssueBaseTx, avaxAssetID, to, amountNAVAX, common.WithContext(ctx)) + return issueSendTx(w.PWallet(), avaxAssetID, to, amountNAVAX, common.WithContext(ctx)) } // Export exports AVAX from P-Chain to another chain (IssueExportTx). func Export(ctx context.Context, w *wallet.Wallet, destChainID ids.ID, amountNAVAX uint64) (ids.ID, error) { avaxAssetID := w.PWallet().Builder().Context().AVAXAssetID - return issueExportTx(w.PWallet().IssueExportTx, destChainID, avaxAssetID, w.PChainAddress(), amountNAVAX, common.WithContext(ctx)) + return issueExportTx(w.PWallet(), destChainID, avaxAssetID, w.PChainAddress(), amountNAVAX, common.WithContext(ctx)) } // Import imports AVAX to P-Chain from another chain (IssueImportTx). func Import(ctx context.Context, w *wallet.Wallet, sourceChainID ids.ID) (ids.ID, error) { - return issueImportTx(w.PWallet().IssueImportTx, sourceChainID, w.PChainAddress(), common.WithContext(ctx)) + return issueImportTx(w.PWallet(), sourceChainID, w.PChainAddress(), common.WithContext(ctx)) } func issueSendTx( - issueBaseTx func(outputs []*avax.TransferableOutput, options ...common.Option) (*txs.Tx, error), + issuer baseTxIssuer, avaxAssetID ids.ID, to ids.ShortID, amountNAVAX uint64, options ...common.Option, ) (ids.ID, error) { - tx, err := issueBaseTx([]*avax.TransferableOutput{{ + tx, err := issuer.IssueBaseTx([]*avax.TransferableOutput{{ Asset: avax.Asset{ID: avaxAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: amountNAVAX, @@ -61,7 +115,7 @@ func issueSendTx( } func issueExportTx( - issueExportTxFn func(chainID ids.ID, outputs []*avax.TransferableOutput, options ...common.Option) (*txs.Tx, error), + issuer exportTxIssuer, destChainID ids.ID, avaxAssetID ids.ID, ownerAddr ids.ShortID, @@ -73,7 +127,7 @@ func issueExportTx( Addrs: []ids.ShortID{ownerAddr}, } - tx, err := issueExportTxFn(destChainID, []*avax.TransferableOutput{{ + tx, err := issuer.IssueExportTx(destChainID, []*avax.TransferableOutput{{ Asset: avax.Asset{ID: avaxAssetID}, Out: &secp256k1fx.TransferOutput{ Amt: amountNAVAX, @@ -87,7 +141,7 @@ func issueExportTx( } func issueImportTx( - issueImportTxFn func(chainID ids.ID, to *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error), + issuer importTxIssuer, sourceChainID ids.ID, ownerAddr ids.ShortID, options ...common.Option, @@ -97,7 +151,7 @@ func issueImportTx( Addrs: []ids.ShortID{ownerAddr}, } - tx, err := issueImportTxFn(sourceChainID, &owner, options...) + tx, err := issuer.IssueImportTx(sourceChainID, &owner, options...) if err != nil { return ids.Empty, fmt.Errorf("failed to issue ImportTx: %w", err) } @@ -160,7 +214,7 @@ type AddPermissionlessValidatorConfig struct { func AddPermissionlessValidator(ctx context.Context, w *wallet.Wallet, cfg AddPermissionlessValidatorConfig) (ids.ID, error) { avaxAssetID := w.PWallet().Builder().Context().AVAXAssetID return issueAddPermissionlessValidatorTx( - w.PWallet().IssueAddPermissionlessValidatorTx, + w.PWallet(), avaxAssetID, cfg, common.WithContext(ctx), @@ -168,15 +222,7 @@ func AddPermissionlessValidator(ctx context.Context, w *wallet.Wallet, cfg AddPe } func issueAddPermissionlessValidatorTx( - issueTxFn func( - vdr *txs.SubnetValidator, - signer signer.Signer, - assetID ids.ID, - validationRewardsOwner *secp256k1fx.OutputOwners, - delegationRewardsOwner *secp256k1fx.OutputOwners, - shares uint32, - options ...common.Option, - ) (*txs.Tx, error), + issuer permissionlessValidatorTxIssuer, avaxAssetID ids.ID, cfg AddPermissionlessValidatorConfig, options ...common.Option, @@ -186,7 +232,7 @@ func issueAddPermissionlessValidatorTx( Addrs: []ids.ShortID{cfg.RewardAddr}, } - tx, err := issueTxFn( + tx, err := issuer.IssueAddPermissionlessValidatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ NodeID: cfg.NodeID, @@ -257,7 +303,7 @@ type AddPermissionlessDelegatorConfig struct { func AddPermissionlessDelegator(ctx context.Context, w *wallet.Wallet, cfg AddPermissionlessDelegatorConfig) (ids.ID, error) { avaxAssetID := w.PWallet().Builder().Context().AVAXAssetID return issueAddPermissionlessDelegatorTx( - w.PWallet().IssueAddPermissionlessDelegatorTx, + w.PWallet(), avaxAssetID, cfg, common.WithContext(ctx), @@ -265,12 +311,7 @@ func AddPermissionlessDelegator(ctx context.Context, w *wallet.Wallet, cfg AddPe } func issueAddPermissionlessDelegatorTx( - issueTxFn func( - vdr *txs.SubnetValidator, - assetID ids.ID, - rewardsOwner *secp256k1fx.OutputOwners, - options ...common.Option, - ) (*txs.Tx, error), + issuer permissionlessDelegatorTxIssuer, avaxAssetID ids.ID, cfg AddPermissionlessDelegatorConfig, options ...common.Option, @@ -280,7 +321,7 @@ func issueAddPermissionlessDelegatorTx( Addrs: []ids.ShortID{cfg.RewardAddr}, } - tx, err := issueTxFn( + tx, err := issuer.IssueAddPermissionlessDelegatorTx( &txs.SubnetValidator{ Validator: txs.Validator{ NodeID: cfg.NodeID, @@ -306,11 +347,11 @@ func issueAddPermissionlessDelegatorTx( // CreateSubnet creates a new subnet (IssueCreateSubnetTx). func CreateSubnet(ctx context.Context, w *wallet.Wallet) (ids.ID, error) { - return issueCreateSubnetTx(w.PWallet().IssueCreateSubnetTx, w.PChainAddress(), common.WithContext(ctx)) + return issueCreateSubnetTx(w.PWallet(), w.PChainAddress(), common.WithContext(ctx)) } func issueCreateSubnetTx( - issueTxFn func(owner *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error), + issuer createSubnetTxIssuer, ownerAddr ids.ShortID, options ...common.Option, ) (ids.ID, error) { @@ -319,7 +360,7 @@ func issueCreateSubnetTx( Addrs: []ids.ShortID{ownerAddr}, } - tx, err := issueTxFn(owner, options...) + tx, err := issuer.IssueCreateSubnetTx(owner, options...) if err != nil { return ids.Empty, fmt.Errorf("failed to issue CreateSubnetTx: %w", err) } @@ -328,11 +369,11 @@ func issueCreateSubnetTx( // TransferSubnetOwnership transfers subnet ownership (IssueTransferSubnetOwnershipTx). func TransferSubnetOwnership(ctx context.Context, w *wallet.Wallet, subnetID ids.ID, newOwner ids.ShortID) (ids.ID, error) { - return issueTransferSubnetOwnershipTx(w.PWallet().IssueTransferSubnetOwnershipTx, subnetID, newOwner, common.WithContext(ctx)) + return issueTransferSubnetOwnershipTx(w.PWallet(), subnetID, newOwner, common.WithContext(ctx)) } func issueTransferSubnetOwnershipTx( - issueTxFn func(subnetID ids.ID, owner *secp256k1fx.OutputOwners, options ...common.Option) (*txs.Tx, error), + issuer transferSubnetOwnershipTxIssuer, subnetID ids.ID, newOwner ids.ShortID, options ...common.Option, @@ -342,7 +383,7 @@ func issueTransferSubnetOwnershipTx( Addrs: []ids.ShortID{newOwner}, } - tx, err := issueTxFn(subnetID, owner, options...) + tx, err := issuer.IssueTransferSubnetOwnershipTx(subnetID, owner, options...) if err != nil { return ids.Empty, fmt.Errorf("failed to issue TransferSubnetOwnershipTx: %w", err) } @@ -351,18 +392,18 @@ func issueTransferSubnetOwnershipTx( // ConvertSubnetToL1 converts a subnet to L1 (IssueConvertSubnetToL1Tx). func ConvertSubnetToL1(ctx context.Context, w *wallet.Wallet, subnetID, chainID ids.ID, managerAddr []byte, validators []*txs.ConvertSubnetToL1Validator) (ids.ID, error) { - return issueConvertSubnetToL1Tx(w.PWallet().IssueConvertSubnetToL1Tx, subnetID, chainID, managerAddr, validators, common.WithContext(ctx)) + return issueConvertSubnetToL1Tx(w.PWallet(), subnetID, chainID, managerAddr, validators, common.WithContext(ctx)) } func issueConvertSubnetToL1Tx( - issueTxFn func(subnetID ids.ID, chainID ids.ID, address []byte, validators []*txs.ConvertSubnetToL1Validator, options ...common.Option) (*txs.Tx, error), + issuer convertSubnetToL1TxIssuer, subnetID ids.ID, chainID ids.ID, managerAddr []byte, validators []*txs.ConvertSubnetToL1Validator, options ...common.Option, ) (ids.ID, error) { - tx, err := issueTxFn(subnetID, chainID, managerAddr, validators, options...) + tx, err := issuer.IssueConvertSubnetToL1Tx(subnetID, chainID, managerAddr, validators, options...) if err != nil { return ids.Empty, fmt.Errorf("failed to issue ConvertSubnetToL1Tx: %w", err) } @@ -424,22 +465,15 @@ type CreateChainConfig struct { // CreateChain creates a new chain on a subnet (IssueCreateChainTx). func CreateChain(ctx context.Context, w *wallet.Wallet, cfg CreateChainConfig) (ids.ID, error) { - return issueCreateChainTx(w.PWallet().IssueCreateChainTx, cfg, common.WithContext(ctx)) + return issueCreateChainTx(w.PWallet(), cfg, common.WithContext(ctx)) } func issueCreateChainTx( - issueTxFn func( - subnetID ids.ID, - genesis []byte, - vmID ids.ID, - fxIDs []ids.ID, - chainName string, - options ...common.Option, - ) (*txs.Tx, error), + issuer createChainTxIssuer, cfg CreateChainConfig, options ...common.Option, ) (ids.ID, error) { - tx, err := issueTxFn( + tx, err := issuer.IssueCreateChainTx( cfg.SubnetID, cfg.Genesis, cfg.VMID, diff --git a/pkg/pchain/pchain_integration_test.go b/pkg/pchain/pchain_integration_test.go index d4fcac3..98f1388 100644 --- a/pkg/pchain/pchain_integration_test.go +++ b/pkg/pchain/pchain_integration_test.go @@ -4,9 +4,9 @@ // This file contains integration tests that run against a tmpnet network. // // To run these tests: -// 1. Build avalanchego: go build -o ./build/avalanchego ./main -// 2. Set AVALANCHEGO_PATH to point to the binary -// 3. Run: go test -tags=integration -v ./pkg/pchain/... +// 1. Build avalanchego: go build -o ./build/avalanchego ./main +// 2. Set AVALANCHEGO_PATH to point to the binary +// 3. Run: go test -tags=integration -v ./pkg/pchain/... package pchain import ( diff --git a/pkg/pchain/pchain_test.go b/pkg/pchain/pchain_test.go index a09a09f..ed42b72 100644 --- a/pkg/pchain/pchain_test.go +++ b/pkg/pchain/pchain_test.go @@ -17,22 +17,174 @@ import ( type testContextKey string +// ============================================================================= +// Issuer Stubs +// ============================================================================= + +// stubBaseTxIssuer implements baseTxIssuer, recording the arguments it was +// called with and returning the configured tx/err. +type stubBaseTxIssuer struct { + tx *txs.Tx + err error + + gotOutputs []*avax.TransferableOutput + gotOpts []common.Option +} + +func (s *stubBaseTxIssuer) IssueBaseTx(outputs []*avax.TransferableOutput, options ...common.Option) (*txs.Tx, error) { + s.gotOutputs = outputs + s.gotOpts = options + return s.tx, s.err +} + +// stubExportTxIssuer implements exportTxIssuer. +type stubExportTxIssuer struct { + tx *txs.Tx + err error + + gotChainID ids.ID + gotOutputs []*avax.TransferableOutput +} + +func (s *stubExportTxIssuer) IssueExportTx(chainID ids.ID, outputs []*avax.TransferableOutput, _ ...common.Option) (*txs.Tx, error) { + s.gotChainID = chainID + s.gotOutputs = outputs + return s.tx, s.err +} + +// stubImportTxIssuer implements importTxIssuer. +type stubImportTxIssuer struct { + tx *txs.Tx + err error + + gotChainID ids.ID + gotOwners *secp256k1fx.OutputOwners +} + +func (s *stubImportTxIssuer) IssueImportTx(chainID ids.ID, to *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { + s.gotChainID = chainID + s.gotOwners = to + return s.tx, s.err +} + +// stubValidatorTxIssuer implements permissionlessValidatorTxIssuer. +type stubValidatorTxIssuer struct { + tx *txs.Tx + err error + + gotVdr *txs.SubnetValidator + gotSigner signer.Signer + gotAssetID ids.ID + gotValidationRewardsOwner *secp256k1fx.OutputOwners + gotDelegationRewardsOwner *secp256k1fx.OutputOwners + gotShares uint32 +} + +func (s *stubValidatorTxIssuer) IssueAddPermissionlessValidatorTx(vdr *txs.SubnetValidator, sig signer.Signer, assetID ids.ID, validationRewardsOwner *secp256k1fx.OutputOwners, delegationRewardsOwner *secp256k1fx.OutputOwners, shares uint32, _ ...common.Option) (*txs.Tx, error) { + s.gotVdr = vdr + s.gotSigner = sig + s.gotAssetID = assetID + s.gotValidationRewardsOwner = validationRewardsOwner + s.gotDelegationRewardsOwner = delegationRewardsOwner + s.gotShares = shares + return s.tx, s.err +} + +// stubDelegatorTxIssuer implements permissionlessDelegatorTxIssuer. +type stubDelegatorTxIssuer struct { + tx *txs.Tx + err error + + gotVdr *txs.SubnetValidator + gotAssetID ids.ID + gotRewardsOwner *secp256k1fx.OutputOwners +} + +func (s *stubDelegatorTxIssuer) IssueAddPermissionlessDelegatorTx(vdr *txs.SubnetValidator, assetID ids.ID, rewardsOwner *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { + s.gotVdr = vdr + s.gotAssetID = assetID + s.gotRewardsOwner = rewardsOwner + return s.tx, s.err +} + +// stubCreateSubnetTxIssuer implements createSubnetTxIssuer. +type stubCreateSubnetTxIssuer struct { + tx *txs.Tx + err error + + gotOwner *secp256k1fx.OutputOwners +} + +func (s *stubCreateSubnetTxIssuer) IssueCreateSubnetTx(owner *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { + s.gotOwner = owner + return s.tx, s.err +} + +// stubTransferSubnetOwnershipTxIssuer implements transferSubnetOwnershipTxIssuer. +type stubTransferSubnetOwnershipTxIssuer struct { + tx *txs.Tx + err error + + gotSubnetID ids.ID + gotOwner *secp256k1fx.OutputOwners +} + +func (s *stubTransferSubnetOwnershipTxIssuer) IssueTransferSubnetOwnershipTx(subnetID ids.ID, owner *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { + s.gotSubnetID = subnetID + s.gotOwner = owner + return s.tx, s.err +} + +// stubConvertSubnetToL1TxIssuer implements convertSubnetToL1TxIssuer. +type stubConvertSubnetToL1TxIssuer struct { + tx *txs.Tx + err error + + gotSubnetID ids.ID + gotChainID ids.ID + gotManagerAddr []byte + gotValidators []*txs.ConvertSubnetToL1Validator +} + +func (s *stubConvertSubnetToL1TxIssuer) IssueConvertSubnetToL1Tx(subnetID ids.ID, chainID ids.ID, address []byte, validators []*txs.ConvertSubnetToL1Validator, _ ...common.Option) (*txs.Tx, error) { + s.gotSubnetID = subnetID + s.gotChainID = chainID + s.gotManagerAddr = address + s.gotValidators = validators + return s.tx, s.err +} + +// stubCreateChainTxIssuer implements createChainTxIssuer. +type stubCreateChainTxIssuer struct { + tx *txs.Tx + err error + + gotCfg CreateChainConfig +} + +func (s *stubCreateChainTxIssuer) IssueCreateChainTx(subnetID ids.ID, genesis []byte, vmID ids.ID, fxIDs []ids.ID, chainName string, _ ...common.Option) (*txs.Tx, error) { + s.gotCfg = CreateChainConfig{ + SubnetID: subnetID, + Genesis: genesis, + VMID: vmID, + FxIDs: fxIDs, + ChainName: chainName, + } + return s.tx, s.err +} + +// ============================================================================= +// Tests +// ============================================================================= + func TestIssueSendTx(t *testing.T) { assetID := ids.GenerateTestID() dest := ids.GenerateTestShortID() amount := uint64(42_000) txID := ids.GenerateTestID() - var captured []*avax.TransferableOutput - gotTxID, err := issueSendTx( - func(outputs []*avax.TransferableOutput, _ ...common.Option) (*txs.Tx, error) { - captured = outputs - return &txs.Tx{TxID: txID}, nil - }, - assetID, - dest, - amount, - ) + issuer := &stubBaseTxIssuer{tx: &txs.Tx{TxID: txID}} + gotTxID, err := issueSendTx(issuer, assetID, dest, amount) if err != nil { t.Fatalf("issueSendTx() returned error: %v", err) } @@ -40,6 +192,7 @@ func TestIssueSendTx(t *testing.T) { t.Fatalf("issueSendTx() txID = %s, want %s", gotTxID, txID) } + captured := issuer.gotOutputs if len(captured) != 1 { t.Fatalf("issueSendTx() output count = %d, want 1", len(captured)) } @@ -63,14 +216,9 @@ func TestIssueSendTx(t *testing.T) { func TestIssueSendTxPassesOptions(t *testing.T) { ctx := context.WithValue(context.Background(), testContextKey("key"), "value") + issuer := &stubBaseTxIssuer{tx: &txs.Tx{TxID: ids.GenerateTestID()}} _, err := issueSendTx( - func(_ []*avax.TransferableOutput, opts ...common.Option) (*txs.Tx, error) { - gotCtx := common.NewOptions(opts).Context() - if gotCtx.Value(testContextKey("key")) != "value" { - t.Fatalf("issueSendTx() context option not propagated") - } - return &txs.Tx{TxID: ids.GenerateTestID()}, nil - }, + issuer, ids.GenerateTestID(), ids.GenerateTestShortID(), 1, @@ -79,14 +227,17 @@ func TestIssueSendTxPassesOptions(t *testing.T) { if err != nil { t.Fatalf("issueSendTx() returned error: %v", err) } + gotCtx := common.NewOptions(issuer.gotOpts).Context() + if gotCtx.Value(testContextKey("key")) != "value" { + t.Fatalf("issueSendTx() context option not propagated") + } } func TestIssueSendTxError(t *testing.T) { expectedErr := errors.New("boom") + issuer := &stubBaseTxIssuer{err: expectedErr} _, err := issueSendTx( - func(_ []*avax.TransferableOutput, _ ...common.Option) (*txs.Tx, error) { - return nil, expectedErr - }, + issuer, ids.GenerateTestID(), ids.GenerateTestShortID(), 1, @@ -106,14 +257,9 @@ func TestIssueExportTx(t *testing.T) { amount := uint64(7) txID := ids.GenerateTestID() - var gotChainID ids.ID - var captured []*avax.TransferableOutput + issuer := &stubExportTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueExportTx( - func(chainID ids.ID, outputs []*avax.TransferableOutput, _ ...common.Option) (*txs.Tx, error) { - gotChainID = chainID - captured = outputs - return &txs.Tx{TxID: txID}, nil - }, + issuer, destChainID, assetID, owner, @@ -125,9 +271,10 @@ func TestIssueExportTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueExportTx() txID = %s, want %s", gotTxID, txID) } - if gotChainID != destChainID { - t.Fatalf("issueExportTx() chainID = %s, want %s", gotChainID, destChainID) + if issuer.gotChainID != destChainID { + t.Fatalf("issueExportTx() chainID = %s, want %s", issuer.gotChainID, destChainID) } + captured := issuer.gotOutputs if len(captured) != 1 { t.Fatalf("issueExportTx() output count = %d, want 1", len(captured)) } @@ -148,14 +295,9 @@ func TestIssueImportTx(t *testing.T) { owner := ids.GenerateTestShortID() txID := ids.GenerateTestID() - var gotChainID ids.ID - var gotOwners *secp256k1fx.OutputOwners + issuer := &stubImportTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueImportTx( - func(chainID ids.ID, to *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { - gotChainID = chainID - gotOwners = to - return &txs.Tx{TxID: txID}, nil - }, + issuer, sourceChainID, owner, ) @@ -165,14 +307,14 @@ func TestIssueImportTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueImportTx() txID = %s, want %s", gotTxID, txID) } - if gotChainID != sourceChainID { - t.Fatalf("issueImportTx() chainID = %s, want %s", gotChainID, sourceChainID) + if issuer.gotChainID != sourceChainID { + t.Fatalf("issueImportTx() chainID = %s, want %s", issuer.gotChainID, sourceChainID) } - if gotOwners == nil { + if issuer.gotOwners == nil { t.Fatal("issueImportTx() owners is nil") } - if len(gotOwners.Addrs) != 1 || gotOwners.Addrs[0] != owner { - t.Fatalf("issueImportTx() owner addrs = %#v, want [%s]", gotOwners.Addrs, owner) + if len(issuer.gotOwners.Addrs) != 1 || issuer.gotOwners.Addrs[0] != owner { + t.Fatalf("issueImportTx() owner addrs = %#v, want [%s]", issuer.gotOwners.Addrs, owner) } } @@ -194,30 +336,9 @@ func TestIssueAddPermissionlessValidatorTx(t *testing.T) { } txID := ids.GenerateTestID() - var gotVdr *txs.SubnetValidator - var gotSigner signer.Signer - var gotAssetID ids.ID - var gotValidationRewardsOwner *secp256k1fx.OutputOwners - var gotDelegationRewardsOwner *secp256k1fx.OutputOwners - var gotShares uint32 + issuer := &stubValidatorTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueAddPermissionlessValidatorTx( - func( - vdr *txs.SubnetValidator, - signer signer.Signer, - assetID ids.ID, - validationRewardsOwner *secp256k1fx.OutputOwners, - delegationRewardsOwner *secp256k1fx.OutputOwners, - shares uint32, - _ ...common.Option, - ) (*txs.Tx, error) { - gotVdr = vdr - gotSigner = signer - gotAssetID = assetID - gotValidationRewardsOwner = validationRewardsOwner - gotDelegationRewardsOwner = delegationRewardsOwner - gotShares = shares - return &txs.Tx{TxID: txID}, nil - }, + issuer, assetID, cfg, ) @@ -227,6 +348,7 @@ func TestIssueAddPermissionlessValidatorTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueAddPermissionlessValidatorTx() txID = %s, want %s", gotTxID, txID) } + gotVdr := issuer.gotVdr if gotVdr == nil { t.Fatal("issueAddPermissionlessValidatorTx() validator is nil") } @@ -243,24 +365,24 @@ func TestIssueAddPermissionlessValidatorTx(t *testing.T) { if gotVdr.Subnet != ids.Empty { t.Fatalf("issueAddPermissionlessValidatorTx() subnet = %s, want Primary Network (ids.Empty)", gotVdr.Subnet) } - gotPop, ok := gotSigner.(*signer.ProofOfPossession) + gotPop, ok := issuer.gotSigner.(*signer.ProofOfPossession) if !ok { - t.Fatalf("issueAddPermissionlessValidatorTx() signer type = %T, want *signer.ProofOfPossession", gotSigner) + t.Fatalf("issueAddPermissionlessValidatorTx() signer type = %T, want *signer.ProofOfPossession", issuer.gotSigner) } if gotPop != pop { t.Fatal("issueAddPermissionlessValidatorTx() signer pointer mismatch") } - if gotAssetID != assetID { - t.Fatalf("issueAddPermissionlessValidatorTx() assetID = %s, want %s", gotAssetID, assetID) + if issuer.gotAssetID != assetID { + t.Fatalf("issueAddPermissionlessValidatorTx() assetID = %s, want %s", issuer.gotAssetID, assetID) } - if gotValidationRewardsOwner == nil || len(gotValidationRewardsOwner.Addrs) != 1 || gotValidationRewardsOwner.Addrs[0] != rewardAddr { - t.Fatalf("issueAddPermissionlessValidatorTx() validation owner addrs = %#v, want [%s]", gotValidationRewardsOwner, rewardAddr) + if issuer.gotValidationRewardsOwner == nil || len(issuer.gotValidationRewardsOwner.Addrs) != 1 || issuer.gotValidationRewardsOwner.Addrs[0] != rewardAddr { + t.Fatalf("issueAddPermissionlessValidatorTx() validation owner addrs = %#v, want [%s]", issuer.gotValidationRewardsOwner, rewardAddr) } - if gotDelegationRewardsOwner == nil || len(gotDelegationRewardsOwner.Addrs) != 1 || gotDelegationRewardsOwner.Addrs[0] != rewardAddr { - t.Fatalf("issueAddPermissionlessValidatorTx() delegation owner addrs = %#v, want [%s]", gotDelegationRewardsOwner, rewardAddr) + if issuer.gotDelegationRewardsOwner == nil || len(issuer.gotDelegationRewardsOwner.Addrs) != 1 || issuer.gotDelegationRewardsOwner.Addrs[0] != rewardAddr { + t.Fatalf("issueAddPermissionlessValidatorTx() delegation owner addrs = %#v, want [%s]", issuer.gotDelegationRewardsOwner, rewardAddr) } - if gotShares != cfg.DelegationFee { - t.Fatalf("issueAddPermissionlessValidatorTx() shares = %d, want %d", gotShares, cfg.DelegationFee) + if issuer.gotShares != cfg.DelegationFee { + t.Fatalf("issueAddPermissionlessValidatorTx() shares = %d, want %d", issuer.gotShares, cfg.DelegationFee) } } @@ -279,21 +401,9 @@ func TestIssueAddPermissionlessDelegatorTx(t *testing.T) { } txID := ids.GenerateTestID() - var gotVdr *txs.SubnetValidator - var gotAssetID ids.ID - var gotRewardsOwner *secp256k1fx.OutputOwners + issuer := &stubDelegatorTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueAddPermissionlessDelegatorTx( - func( - vdr *txs.SubnetValidator, - assetID ids.ID, - rewardsOwner *secp256k1fx.OutputOwners, - _ ...common.Option, - ) (*txs.Tx, error) { - gotVdr = vdr - gotAssetID = assetID - gotRewardsOwner = rewardsOwner - return &txs.Tx{TxID: txID}, nil - }, + issuer, assetID, cfg, ) @@ -303,6 +413,7 @@ func TestIssueAddPermissionlessDelegatorTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueAddPermissionlessDelegatorTx() txID = %s, want %s", gotTxID, txID) } + gotVdr := issuer.gotVdr if gotVdr == nil { t.Fatal("issueAddPermissionlessDelegatorTx() validator is nil") } @@ -315,11 +426,11 @@ func TestIssueAddPermissionlessDelegatorTx(t *testing.T) { if gotVdr.Subnet != ids.Empty { t.Fatalf("issueAddPermissionlessDelegatorTx() subnet = %s, want Primary Network (ids.Empty)", gotVdr.Subnet) } - if gotAssetID != assetID { - t.Fatalf("issueAddPermissionlessDelegatorTx() assetID = %s, want %s", gotAssetID, assetID) + if issuer.gotAssetID != assetID { + t.Fatalf("issueAddPermissionlessDelegatorTx() assetID = %s, want %s", issuer.gotAssetID, assetID) } - if gotRewardsOwner == nil || len(gotRewardsOwner.Addrs) != 1 || gotRewardsOwner.Addrs[0] != rewardAddr { - t.Fatalf("issueAddPermissionlessDelegatorTx() rewards owner addrs = %#v, want [%s]", gotRewardsOwner, rewardAddr) + if issuer.gotRewardsOwner == nil || len(issuer.gotRewardsOwner.Addrs) != 1 || issuer.gotRewardsOwner.Addrs[0] != rewardAddr { + t.Fatalf("issueAddPermissionlessDelegatorTx() rewards owner addrs = %#v, want [%s]", issuer.gotRewardsOwner, rewardAddr) } } @@ -327,12 +438,9 @@ func TestIssueCreateSubnetTx(t *testing.T) { owner := ids.GenerateTestShortID() txID := ids.GenerateTestID() - var gotOwner *secp256k1fx.OutputOwners + issuer := &stubCreateSubnetTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueCreateSubnetTx( - func(owner *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { - gotOwner = owner - return &txs.Tx{TxID: txID}, nil - }, + issuer, owner, ) if err != nil { @@ -341,8 +449,8 @@ func TestIssueCreateSubnetTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueCreateSubnetTx() txID = %s, want %s", gotTxID, txID) } - if gotOwner == nil || len(gotOwner.Addrs) != 1 || gotOwner.Addrs[0] != owner { - t.Fatalf("issueCreateSubnetTx() owner addrs = %#v, want [%s]", gotOwner, owner) + if issuer.gotOwner == nil || len(issuer.gotOwner.Addrs) != 1 || issuer.gotOwner.Addrs[0] != owner { + t.Fatalf("issueCreateSubnetTx() owner addrs = %#v, want [%s]", issuer.gotOwner, owner) } } @@ -351,14 +459,9 @@ func TestIssueTransferSubnetOwnershipTx(t *testing.T) { newOwner := ids.GenerateTestShortID() txID := ids.GenerateTestID() - var gotSubnetID ids.ID - var gotOwner *secp256k1fx.OutputOwners + issuer := &stubTransferSubnetOwnershipTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueTransferSubnetOwnershipTx( - func(subnetID ids.ID, owner *secp256k1fx.OutputOwners, _ ...common.Option) (*txs.Tx, error) { - gotSubnetID = subnetID - gotOwner = owner - return &txs.Tx{TxID: txID}, nil - }, + issuer, subnetID, newOwner, ) @@ -368,11 +471,11 @@ func TestIssueTransferSubnetOwnershipTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueTransferSubnetOwnershipTx() txID = %s, want %s", gotTxID, txID) } - if gotSubnetID != subnetID { - t.Fatalf("issueTransferSubnetOwnershipTx() subnetID = %s, want %s", gotSubnetID, subnetID) + if issuer.gotSubnetID != subnetID { + t.Fatalf("issueTransferSubnetOwnershipTx() subnetID = %s, want %s", issuer.gotSubnetID, subnetID) } - if gotOwner == nil || len(gotOwner.Addrs) != 1 || gotOwner.Addrs[0] != newOwner { - t.Fatalf("issueTransferSubnetOwnershipTx() owner addrs = %#v, want [%s]", gotOwner, newOwner) + if issuer.gotOwner == nil || len(issuer.gotOwner.Addrs) != 1 || issuer.gotOwner.Addrs[0] != newOwner { + t.Fatalf("issueTransferSubnetOwnershipTx() owner addrs = %#v, want [%s]", issuer.gotOwner, newOwner) } } @@ -383,24 +486,9 @@ func TestIssueConvertSubnetToL1Tx(t *testing.T) { validators := []*txs.ConvertSubnetToL1Validator{{NodeID: []byte{0xAA}, Weight: 1}} txID := ids.GenerateTestID() - var gotSubnetID ids.ID - var gotChainID ids.ID - var gotManagerAddr []byte - var gotValidators []*txs.ConvertSubnetToL1Validator + issuer := &stubConvertSubnetToL1TxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueConvertSubnetToL1Tx( - func( - subnetID ids.ID, - chainID ids.ID, - address []byte, - validators []*txs.ConvertSubnetToL1Validator, - _ ...common.Option, - ) (*txs.Tx, error) { - gotSubnetID = subnetID - gotChainID = chainID - gotManagerAddr = address - gotValidators = validators - return &txs.Tx{TxID: txID}, nil - }, + issuer, subnetID, chainID, managerAddr, @@ -412,14 +500,15 @@ func TestIssueConvertSubnetToL1Tx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueConvertSubnetToL1Tx() txID = %s, want %s", gotTxID, txID) } - if gotSubnetID != subnetID || gotChainID != chainID { - t.Fatalf("issueConvertSubnetToL1Tx() IDs = (%s,%s), want (%s,%s)", gotSubnetID, gotChainID, subnetID, chainID) + if issuer.gotSubnetID != subnetID || issuer.gotChainID != chainID { + t.Fatalf("issueConvertSubnetToL1Tx() IDs = (%s,%s), want (%s,%s)", issuer.gotSubnetID, issuer.gotChainID, subnetID, chainID) } + gotManagerAddr := issuer.gotManagerAddr if len(gotManagerAddr) != len(managerAddr) || gotManagerAddr[0] != managerAddr[0] || gotManagerAddr[1] != managerAddr[1] { t.Fatalf("issueConvertSubnetToL1Tx() managerAddr = %x, want %x", gotManagerAddr, managerAddr) } - if len(gotValidators) != 1 || gotValidators[0] != validators[0] { - t.Fatalf("issueConvertSubnetToL1Tx() validators = %#v, want %#v", gotValidators, validators) + if len(issuer.gotValidators) != 1 || issuer.gotValidators[0] != validators[0] { + t.Fatalf("issueConvertSubnetToL1Tx() validators = %#v, want %#v", issuer.gotValidators, validators) } } @@ -433,25 +522,9 @@ func TestIssueCreateChainTx(t *testing.T) { } txID := ids.GenerateTestID() - var gotCfg CreateChainConfig + issuer := &stubCreateChainTxIssuer{tx: &txs.Tx{TxID: txID}} gotTxID, err := issueCreateChainTx( - func( - subnetID ids.ID, - genesis []byte, - vmID ids.ID, - fxIDs []ids.ID, - chainName string, - _ ...common.Option, - ) (*txs.Tx, error) { - gotCfg = CreateChainConfig{ - SubnetID: subnetID, - Genesis: genesis, - VMID: vmID, - FxIDs: fxIDs, - ChainName: chainName, - } - return &txs.Tx{TxID: txID}, nil - }, + issuer, cfg, ) if err != nil { @@ -460,6 +533,7 @@ func TestIssueCreateChainTx(t *testing.T) { if gotTxID != txID { t.Fatalf("issueCreateChainTx() txID = %s, want %s", gotTxID, txID) } + gotCfg := issuer.gotCfg if gotCfg.SubnetID != cfg.SubnetID || gotCfg.VMID != cfg.VMID || gotCfg.ChainName != cfg.ChainName { t.Fatalf("issueCreateChainTx() config mismatch: got %#v, want %#v", gotCfg, cfg) } diff --git a/pkg/wallet/keychain.go b/pkg/wallet/keychain.go index 7605df9..2a2e65d 100644 --- a/pkg/wallet/keychain.go +++ b/pkg/wallet/keychain.go @@ -100,7 +100,6 @@ func deriveEthAddress(key *secp256k1.PrivateKey) string { return key.PublicKey().EthAddress().Hex() } - // KeyToHex converts a private key to hex format with 0x prefix. func KeyToHex(key *secp256k1.PrivateKey) string { return "0x" + hex.EncodeToString(key.Bytes()) diff --git a/pkg/wallet/ledger.go b/pkg/wallet/ledger.go index c9e8340..fd583f0 100644 --- a/pkg/wallet/ledger.go +++ b/pkg/wallet/ledger.go @@ -36,9 +36,9 @@ const LedgerEnabled = true type LedgerKeychain struct { device *ledger.LedgerAvalanche index uint32 - address ids.ShortID // P-Chain address (from 9000 path) - pubKey *secp256k1.PublicKey // Public key from m/44'/9000'/0'/0/{index} - evmPubKey *secp256k1.PublicKey // Public key from m/44'/60'/0'/0/{index} + address ids.ShortID // P-Chain address (from 9000 path) + pubKey *secp256k1.PublicKey // Public key from m/44'/9000'/0'/0/{index} + evmPubKey *secp256k1.PublicKey // Public key from m/44'/60'/0'/0/{index} addresses set.Set[ids.ShortID] } diff --git a/pkg/wallet/wallet.go b/pkg/wallet/wallet.go index d0a6ec1..c7c580b 100644 --- a/pkg/wallet/wallet.go +++ b/pkg/wallet/wallet.go @@ -20,11 +20,11 @@ import ( // Wallet wraps the avalanchego wallet for P-Chain operations. type Wallet struct { - key *secp256k1.PrivateKey // nil for Ledger - keychain *secp256k1fx.Keychain // nil for Ledger - pWallet pwallet.Wallet - config network.Config - address ids.ShortID // used when key is nil (Ledger mode) + key *secp256k1.PrivateKey // nil for Ledger + keychain *secp256k1fx.Keychain // nil for Ledger + pWallet pwallet.Wallet + config network.Config + address ids.ShortID // used when key is nil (Ledger mode) } // NewWallet creates a new wallet for P-Chain operations. @@ -147,12 +147,12 @@ func (w *Wallet) Config() network.Config { // FullWallet wraps the avalanchego primary.Wallet for multi-chain operations. type FullWallet struct { - key *secp256k1.PrivateKey // nil for Ledger - keychain *secp256k1fx.Keychain // nil for Ledger - wallet *primary.Wallet - config network.Config - address ids.ShortID // P-Chain address (used when key is nil) - ethAddr common.Address // C-Chain address (used when key is nil) + key *secp256k1.PrivateKey // nil for Ledger + keychain *secp256k1fx.Keychain // nil for Ledger + wallet *primary.Wallet + config network.Config + address ids.ShortID // P-Chain address (used when key is nil) + ethAddr common.Address // C-Chain address (used when key is nil) } // NewFullWallet creates a new wallet for multi-chain operations (P-Chain and C-Chain). From cdbdf0ded95b650940517ebb3cb1ba444f56ed28 Mon Sep 17 00:00:00 2001 From: Owen Date: Wed, 10 Jun 2026 14:11:26 -0400 Subject: [PATCH 2/2] fix: upgrade Go to 1.25.11 and golang.org/x deps to resolve stdlib vulnerabilities govulncheck fails CI on main: GO-2026-5037 (crypto/x509), GO-2026-4971 (net), GO-2026-4918 (net/http + golang.org/x/net) are reachable from keystore key generation, wallet dialing, and network ID lookup. All are fixed in go1.25.10/1.25.11 and x/net v0.53.0. - go.mod: go 1.25.9 -> 1.25.11; x/net v0.47.0 -> v0.53.0 (pulls x/crypto, x/sys, x/term, x/text et al. forward) - ci.yml: bump setup-go and GOTOOLCHAIN pins to 1.25.11 govulncheck now reports 0 reachable vulnerabilities. --- .github/workflows/ci.yml | 8 ++++---- go.mod | 18 +++++++++--------- go.sum | 32 ++++++++++++++++---------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f0631d..220dfd0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: - go-version: "1.25.9" + go-version: "1.25.11" - name: Verify go.mod is tidy run: | @@ -28,17 +28,17 @@ jobs: - name: Run staticcheck env: - GOTOOLCHAIN: go1.25.9 + GOTOOLCHAIN: go1.25.11 run: go run honnef.co/go/tools/cmd/staticcheck@v0.6.1 ./... - name: Run staticcheck (tagged e2e) env: - GOTOOLCHAIN: go1.25.9 + GOTOOLCHAIN: go1.25.11 run: go run honnef.co/go/tools/cmd/staticcheck@v0.6.1 -tags=clie2e,networke2e ./e2e/... - name: Run govulncheck env: - GOTOOLCHAIN: go1.25.9 + GOTOOLCHAIN: go1.25.11 run: go run golang.org/x/vuln/cmd/govulncheck@v1.2.0 ./... - name: Run tests diff --git a/go.mod b/go.mod index cd125df..6226294 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,14 @@ module github.com/ava-labs/platform-cli -go 1.25.9 +go 1.25.11 require ( github.com/ava-labs/avalanchego v1.14.1 github.com/ava-labs/ledger-avalanche-go v1.1.0 github.com/ava-labs/libevm v1.13.15-0.20251210210615-b8e76562a300 github.com/spf13/cobra v1.9.1 - golang.org/x/crypto v0.45.0 - golang.org/x/term v0.37.0 + golang.org/x/crypto v0.50.0 + golang.org/x/term v0.42.0 ) require ( @@ -119,14 +119,14 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.53.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/tools v0.43.0 // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect diff --git a/go.sum b/go.sum index 3c9e24a..f3c417e 100644 --- a/go.sum +++ b/go.sum @@ -560,8 +560,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e h1:4qufH0hlUYs6AO6XmZC3GqfDPGSXHVXUFR6OND+iJX4= golang.org/x/exp v0.0.0-20241215155358-4a5509556b9e/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= @@ -573,8 +573,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -598,8 +598,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= @@ -610,8 +610,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -647,20 +647,20 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= @@ -678,8 +678,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=