diff --git a/vms/avm/block/executor/block.go b/vms/avm/block/executor/block.go index 8663f27ba12..e49f1f482f8 100644 --- a/vms/avm/block/executor/block.go +++ b/vms/avm/block/executor/block.go @@ -77,8 +77,9 @@ func (b *Block) Verify(context.Context) error { // before performing any possible DB reads. for _, tx := range txs { err := tx.Unsigned.Visit(&executor.SyntacticVerifier{ - Backend: b.manager.backend, - Tx: tx, + Backend: b.manager.backend, + BlkTimestamp: newChainTime, + Tx: tx, }) if err != nil { txID := tx.ID() diff --git a/vms/avm/block/executor/manager.go b/vms/avm/block/executor/manager.go index 9822743b7fd..a2a981c5185 100644 --- a/vms/avm/block/executor/manager.go +++ b/vms/avm/block/executor/manager.go @@ -148,8 +148,9 @@ func (m *manager) VerifyTx(tx *txs.Tx) error { } err := tx.Unsigned.Visit(&executor.SyntacticVerifier{ - Backend: m.backend, - Tx: tx, + Backend: m.backend, + BlkTimestamp: m.state.GetTimestamp(), + Tx: tx, }) if err != nil { return err diff --git a/vms/avm/block/executor/manager_test.go b/vms/avm/block/executor/manager_test.go index f0200f398a6..5121ca6f440 100644 --- a/vms/avm/block/executor/manager_test.go +++ b/vms/avm/block/executor/manager_test.go @@ -145,7 +145,9 @@ func TestManagerVerifyTx(t *testing.T) { Unsigned: unsigned, } }, - managerF: func(*gomock.Controller) *manager { + managerF: func(ctrl *gomock.Controller) *manager { + state := state.NewMockState(ctrl) + state.EXPECT().GetTimestamp().Return(time.Time{}) return &manager{ backend: &executor.Backend{ Bootstrapped: true, @@ -154,6 +156,7 @@ func TestManagerVerifyTx(t *testing.T) { EUpgradeTime: mockable.MaxTime, }, }, + state: state, } }, expectedErr: errTestSyntacticVerifyFail, @@ -176,7 +179,7 @@ func TestManagerVerifyTx(t *testing.T) { // These values don't matter for this test state := state.NewMockState(ctrl) state.EXPECT().GetLastAccepted().Return(lastAcceptedID) - state.EXPECT().GetTimestamp().Return(time.Time{}) + state.EXPECT().GetTimestamp().Return(time.Time{}).Times(2) return &manager{ backend: &executor.Backend{ @@ -212,7 +215,7 @@ func TestManagerVerifyTx(t *testing.T) { // These values don't matter for this test state := state.NewMockState(ctrl) state.EXPECT().GetLastAccepted().Return(lastAcceptedID) - state.EXPECT().GetTimestamp().Return(time.Time{}) + state.EXPECT().GetTimestamp().Return(time.Time{}).Times(2) return &manager{ backend: &executor.Backend{ @@ -248,7 +251,7 @@ func TestManagerVerifyTx(t *testing.T) { // These values don't matter for this test state := state.NewMockState(ctrl) state.EXPECT().GetLastAccepted().Return(lastAcceptedID) - state.EXPECT().GetTimestamp().Return(time.Time{}) + state.EXPECT().GetTimestamp().Return(time.Time{}).Times(2) return &manager{ backend: &executor.Backend{ diff --git a/vms/avm/txs/executor/semantic_verifier.go b/vms/avm/txs/executor/semantic_verifier.go index 946346bc064..0b807008e0d 100644 --- a/vms/avm/txs/executor/semantic_verifier.go +++ b/vms/avm/txs/executor/semantic_verifier.go @@ -11,6 +11,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/vms/avm/state" "github.com/ava-labs/avalanchego/vms/avm/txs" + "github.com/ava-labs/avalanchego/vms/avm/txs/fees" "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" ) @@ -31,28 +32,7 @@ type SemanticVerifier struct { } func (v *SemanticVerifier) BaseTx(tx *txs.BaseTx) error { - for i, in := range tx.Ins { - // Note: Verification of the length of [t.tx.Creds] happens during - // syntactic verification, which happens before semantic verification. - cred := v.Tx.Creds[i].Credential - if err := v.verifyTransfer(tx, in, cred); err != nil { - return err - } - } - - for _, out := range tx.Outs { - fxIndex, err := v.getFx(out.Out) - if err != nil { - return err - } - - assetID := out.AssetID() - if err := v.verifyFxUsage(fxIndex, assetID); err != nil { - return err - } - } - - return nil + return v.verifyBaseTx(tx, nil, nil) } func (v *SemanticVerifier) CreateAssetTx(tx *txs.CreateAssetTx) error { @@ -60,7 +40,7 @@ func (v *SemanticVerifier) CreateAssetTx(tx *txs.CreateAssetTx) error { } func (v *SemanticVerifier) OperationTx(tx *txs.OperationTx) error { - if err := v.BaseTx(&tx.BaseTx); err != nil { + if err := v.verifyBaseTx(&tx.BaseTx, nil, nil); err != nil { return err } @@ -81,7 +61,7 @@ func (v *SemanticVerifier) OperationTx(tx *txs.OperationTx) error { } func (v *SemanticVerifier) ImportTx(tx *txs.ImportTx) error { - if err := v.BaseTx(&tx.BaseTx); err != nil { + if err := v.verifyBaseTx(&tx.BaseTx, tx.ImportedIns, nil); err != nil { return err } @@ -122,7 +102,7 @@ func (v *SemanticVerifier) ImportTx(tx *txs.ImportTx) error { } func (v *SemanticVerifier) ExportTx(tx *txs.ExportTx) error { - if err := v.BaseTx(&tx.BaseTx); err != nil { + if err := v.verifyBaseTx(&tx.BaseTx, nil, tx.ExportedOuts); err != nil { return err } @@ -146,6 +126,53 @@ func (v *SemanticVerifier) ExportTx(tx *txs.ExportTx) error { return nil } +func (v *SemanticVerifier) verifyBaseTx( + tx *txs.BaseTx, + importedIns []*avax.TransferableInput, + exportedOuts []*avax.TransferableOutput, +) error { + feeCalculator := fees.Calculator{ + Config: v.Config, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + + err := avax.VerifyTx( + feeCalculator.Fee, + v.FeeAssetID, + [][]*avax.TransferableInput{tx.Ins, importedIns}, + [][]*avax.TransferableOutput{tx.Outs, exportedOuts}, + v.Codec, + ) + if err != nil { + return err + } + + for i, in := range tx.Ins { + // Note: Verification of the length of [t.tx.Creds] happens during + // syntactic verification, which happens before semantic verification. + cred := v.Tx.Creds[i].Credential + if err := v.verifyTransfer(tx, in, cred); err != nil { + return err + } + } + + for _, out := range tx.Outs { + fxIndex, err := v.getFx(out.Out) + if err != nil { + return err + } + + assetID := out.AssetID() + if err := v.verifyFxUsage(fxIndex, assetID); err != nil { + return err + } + } + + return nil +} + func (v *SemanticVerifier) verifyTransfer( tx txs.UnsignedTx, in *avax.TransferableInput, diff --git a/vms/avm/txs/executor/syntactic_verifier.go b/vms/avm/txs/executor/syntactic_verifier.go index 81a2f2a715f..b253aba427e 100644 --- a/vms/avm/txs/executor/syntactic_verifier.go +++ b/vms/avm/txs/executor/syntactic_verifier.go @@ -7,13 +7,13 @@ import ( "errors" "fmt" "strings" + "time" "unicode" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/avm/txs" - "github.com/ava-labs/avalanchego/vms/components/avax" ) const ( @@ -47,7 +47,8 @@ var ( type SyntacticVerifier struct { *Backend - Tx *txs.Tx + BlkTimestamp time.Time + Tx *txs.Tx } func (v *SyntacticVerifier) BaseTx(tx *txs.BaseTx) error { @@ -55,17 +56,6 @@ func (v *SyntacticVerifier) BaseTx(tx *txs.BaseTx) error { return err } - err := avax.VerifyTx( - v.Config.TxFee, - v.FeeAssetID, - [][]*avax.TransferableInput{tx.Ins}, - [][]*avax.TransferableOutput{tx.Outs}, - v.Codec, - ) - if err != nil { - return err - } - for _, cred := range v.Tx.Creds { if err := cred.Verify(); err != nil { return err @@ -118,17 +108,6 @@ func (v *SyntacticVerifier) CreateAssetTx(tx *txs.CreateAssetTx) error { return err } - err := avax.VerifyTx( - v.Config.CreateAssetTxFee, - v.FeeAssetID, - [][]*avax.TransferableInput{tx.Ins}, - [][]*avax.TransferableOutput{tx.Outs}, - v.Codec, - ) - if err != nil { - return err - } - for _, state := range tx.States { if err := state.Verify(v.Codec, len(v.Fxs)); err != nil { return err @@ -166,17 +145,6 @@ func (v *SyntacticVerifier) OperationTx(tx *txs.OperationTx) error { return err } - err := avax.VerifyTx( - v.Config.TxFee, - v.FeeAssetID, - [][]*avax.TransferableInput{tx.Ins}, - [][]*avax.TransferableOutput{tx.Outs}, - v.Codec, - ) - if err != nil { - return err - } - inputs := set.NewSet[ids.ID](len(tx.Ins)) for _, in := range tx.Ins { inputs.Add(in.InputID()) @@ -226,20 +194,6 @@ func (v *SyntacticVerifier) ImportTx(tx *txs.ImportTx) error { return err } - err := avax.VerifyTx( - v.Config.TxFee, - v.FeeAssetID, - [][]*avax.TransferableInput{ - tx.Ins, - tx.ImportedIns, - }, - [][]*avax.TransferableOutput{tx.Outs}, - v.Codec, - ) - if err != nil { - return err - } - for _, cred := range v.Tx.Creds { if err := cred.Verify(); err != nil { return err @@ -268,20 +222,6 @@ func (v *SyntacticVerifier) ExportTx(tx *txs.ExportTx) error { return err } - err := avax.VerifyTx( - v.Config.TxFee, - v.FeeAssetID, - [][]*avax.TransferableInput{tx.Ins}, - [][]*avax.TransferableOutput{ - tx.Outs, - tx.ExportedOuts, - }, - v.Codec, - ) - if err != nil { - return err - } - for _, cred := range v.Tx.Creds { if err := cred.Verify(); err != nil { return err diff --git a/vms/avm/txs/executor/syntactic_verifier_test.go b/vms/avm/txs/executor/syntactic_verifier_test.go index cc160ed7982..30a36fadc18 100644 --- a/vms/avm/txs/executor/syntactic_verifier_test.go +++ b/vms/avm/txs/executor/syntactic_verifier_test.go @@ -4,7 +4,6 @@ package executor import ( - "math" "strings" "testing" "time" @@ -22,8 +21,6 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/secp256k1fx" - - safemath "github.com/ava-labs/avalanchego/utils/math" ) var ( @@ -163,177 +160,177 @@ func TestSyntacticVerifierBaseTx(t *testing.T) { }, err: avax.ErrMemoTooLarge, }, - { - name: "invalid output", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: 0, - OutputOwners: outputOwners, - } - - baseTx := baseTx - baseTx.Outs = []*avax.TransferableOutput{ - &output, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueOutput, - }, - { - name: "unsorted outputs", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] - - baseTx := baseTx - baseTx.Outs = outputs - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: avax.ErrOutputsNotSorted, - }, - { - name: "invalid input", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 0, - Input: inputSigners, - } - - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueInput, - }, - { - name: "duplicate inputs", - txFunc: func() *txs.Tx { - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "input overflow", - txFunc: func() *txs.Tx { - input0 := input - input0.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - input1 := input - input1.UTXOID.OutputIndex++ - input1.In = &secp256k1fx.TransferInput{ - Amt: math.MaxUint64, - Input: inputSigners, - } - - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input0, - &input1, - } - avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "output overflow", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: math.MaxUint64, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - - baseTx := baseTx - baseTx.Outs = outputs - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "invalid output", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: 0, + // OutputOwners: outputOwners, + // } + + // baseTx := baseTx + // baseTx.Outs = []*avax.TransferableOutput{ + // &output, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueOutput, + // }, + // { + // name: "unsorted outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // baseTx := baseTx + // baseTx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "invalid input", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 0, + // Input: inputSigners, + // } + + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueInput, + // }, + // { + // name: "duplicate inputs", + // txFunc: func() *txs.Tx { + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "input overflow", + // txFunc: func() *txs.Tx { + // input0 := input + // input0.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // input1 := input + // input1.UTXOID.OutputIndex++ + // input1.In = &secp256k1fx.TransferInput{ + // Amt: math.MaxUint64, + // Input: inputSigners, + // } + + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input0, + // &input1, + // } + // avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "output overflow", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: math.MaxUint64, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + + // baseTx := baseTx + // baseTx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, { name: "invalid credential", txFunc: func() *txs.Tx { @@ -355,46 +352,46 @@ func TestSyntacticVerifierBaseTx(t *testing.T) { }, err: errWrongNumberOfCredentials, }, - { - name: "barely sufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee, - Input: inputSigners, - } - - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: nil, - }, - { - name: "barely insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee - 1, - Input: inputSigners, - } - - baseTx := baseTx - baseTx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &txs.BaseTx{BaseTx: baseTx}, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "barely sufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee, + // Input: inputSigners, + // } + + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: nil, + // }, + // { + // name: "barely insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee - 1, + // Input: inputSigners, + // } + + // baseTx := baseTx + // baseTx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &txs.BaseTx{BaseTx: baseTx}, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -659,177 +656,177 @@ func TestSyntacticVerifierCreateAssetTx(t *testing.T) { }, err: avax.ErrMemoTooLarge, }, - { - name: "invalid output", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: 0, - OutputOwners: outputOwners, - } - - tx := tx - tx.Outs = []*avax.TransferableOutput{ - &output, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueOutput, - }, - { - name: "unsorted outputs", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrOutputsNotSorted, - }, - { - name: "invalid input", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 0, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueInput, - }, - { - name: "duplicate inputs", - txFunc: func() *txs.Tx { - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "input overflow", - txFunc: func() *txs.Tx { - input0 := input - input0.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - input1 := input - input1.UTXOID.OutputIndex++ - input1.In = &secp256k1fx.TransferInput{ - Amt: math.MaxUint64, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input0, - &input1, - } - avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "output overflow", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: math.MaxUint64, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "invalid output", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: 0, + // OutputOwners: outputOwners, + // } + + // tx := tx + // tx.Outs = []*avax.TransferableOutput{ + // &output, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueOutput, + // }, + // { + // name: "unsorted outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "invalid input", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 0, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueInput, + // }, + // { + // name: "duplicate inputs", + // txFunc: func() *txs.Tx { + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "input overflow", + // txFunc: func() *txs.Tx { + // input0 := input + // input0.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // input1 := input + // input1.UTXOID.OutputIndex++ + // input1.In = &secp256k1fx.TransferInput{ + // Amt: math.MaxUint64, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input0, + // &input1, + // } + // avax.SortTransferableInputsWithSigners(baseTx.Ins, make([][]*secp256k1.PrivateKey, 2)) + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "output overflow", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: math.MaxUint64, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, { name: "invalid nil state", txFunc: func() *txs.Tx { @@ -965,46 +962,46 @@ func TestSyntacticVerifierCreateAssetTx(t *testing.T) { }, err: errWrongNumberOfCredentials, }, - { - name: "barely sufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: nil, - }, - { - name: "barely insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee - 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "barely sufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: nil, + // }, + // { + // name: "barely insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.CreateAssetTxFee - 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -1181,220 +1178,220 @@ func TestSyntacticVerifierOperationTx(t *testing.T) { }, err: avax.ErrMemoTooLarge, }, + // { + // name: "invalid output", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: 0, + // OutputOwners: outputOwners, + // } + + // tx := tx + // tx.Outs = []*avax.TransferableOutput{ + // &output, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueOutput, + // }, + // { + // name: "unsorted outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "invalid input", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 0, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueInput, + // }, + // { + // name: "duplicate inputs", + // txFunc: func() *txs.Tx { + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "input overflow", + // txFunc: func() *txs.Tx { + // input0 := input + // input0.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // input1 := input + // input1.UTXOID.OutputIndex++ + // input1.In = &secp256k1fx.TransferInput{ + // Amt: math.MaxUint64, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input0, + // &input1, + // } + // avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "output overflow", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: math.MaxUint64, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output, + // } + // avax.SortTransferableOutputs(outputs, codec) + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, { - name: "invalid output", + name: "invalid nil op", txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: 0, - OutputOwners: outputOwners, - } - tx := tx - tx.Outs = []*avax.TransferableOutput{ - &output, + tx.Ops = []*txs.Operation{ + nil, } return &txs.Tx{ Unsigned: &tx, Creds: creds, } }, - err: secp256k1fx.ErrNoValueOutput, + err: txs.ErrNilOperation, }, { - name: "unsorted outputs", + name: "invalid nil fx op", txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] + op := op + op.Op = nil tx := tx - tx.Outs = outputs + tx.Ops = []*txs.Operation{ + &op, + } return &txs.Tx{ Unsigned: &tx, Creds: creds, } }, - err: avax.ErrOutputsNotSorted, + err: txs.ErrNilFxOperation, }, { - name: "invalid input", + name: "invalid duplicated op UTXOs", txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 0, - Input: inputSigners, + op := op + op.UTXOIDs = []*avax.UTXOID{ + &opUTXOID, + &opUTXOID, } tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, + tx.Ops = []*txs.Operation{ + &op, } return &txs.Tx{ Unsigned: &tx, Creds: creds, } }, - err: secp256k1fx.ErrNoValueInput, - }, - { - name: "duplicate inputs", - txFunc: func() *txs.Tx { - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "input overflow", - txFunc: func() *txs.Tx { - input0 := input - input0.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - input1 := input - input1.UTXOID.OutputIndex++ - input1.In = &secp256k1fx.TransferInput{ - Amt: math.MaxUint64, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input0, - &input1, - } - avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "output overflow", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: math.MaxUint64, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output, - } - avax.SortTransferableOutputs(outputs, codec) - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, - { - name: "invalid nil op", - txFunc: func() *txs.Tx { - tx := tx - tx.Ops = []*txs.Operation{ - nil, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: txs.ErrNilOperation, - }, - { - name: "invalid nil fx op", - txFunc: func() *txs.Tx { - op := op - op.Op = nil - - tx := tx - tx.Ops = []*txs.Operation{ - &op, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: txs.ErrNilFxOperation, - }, - { - name: "invalid duplicated op UTXOs", - txFunc: func() *txs.Tx { - op := op - op.UTXOIDs = []*avax.UTXOID{ - &opUTXOID, - &opUTXOID, - } - - tx := tx - tx.Ops = []*txs.Operation{ - &op, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: txs.ErrNotSortedAndUniqueUTXOIDs, + err: txs.ErrNotSortedAndUniqueUTXOIDs, }, { name: "invalid duplicated UTXOs across ops", @@ -1455,46 +1452,46 @@ func TestSyntacticVerifierOperationTx(t *testing.T) { }, err: errWrongNumberOfCredentials, }, - { - name: "barely sufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: nil, - }, - { - name: "barely insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee - 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "barely sufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: nil, + // }, + // { + // name: "barely insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee - 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -1652,189 +1649,189 @@ func TestSyntacticVerifierImportTx(t *testing.T) { }, err: avax.ErrMemoTooLarge, }, - { - name: "invalid output", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: 0, - OutputOwners: outputOwners, - } - - tx := tx - tx.Outs = []*avax.TransferableOutput{ - &output, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueOutput, - }, - { - name: "unsorted outputs", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrOutputsNotSorted, - }, - { - name: "invalid input", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 0, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueInput, - }, - { - name: "duplicate inputs", - txFunc: func() *txs.Tx { - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "duplicate imported inputs", - txFunc: func() *txs.Tx { - tx := tx - tx.ImportedIns = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "input overflow", - txFunc: func() *txs.Tx { - input0 := input - input0.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - input1 := input - input1.UTXOID.OutputIndex++ - input1.In = &secp256k1fx.TransferInput{ - Amt: math.MaxUint64, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input0, - &input1, - } - avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "output overflow", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: math.MaxUint64, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output, - } - avax.SortTransferableOutputs(outputs, codec) - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - tx := tx - tx.ImportedIns = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "invalid output", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: 0, + // OutputOwners: outputOwners, + // } + + // tx := tx + // tx.Outs = []*avax.TransferableOutput{ + // &output, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueOutput, + // }, + // { + // name: "unsorted outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "invalid input", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 0, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueInput, + // }, + // { + // name: "duplicate inputs", + // txFunc: func() *txs.Tx { + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "duplicate imported inputs", + // txFunc: func() *txs.Tx { + // tx := tx + // tx.ImportedIns = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "input overflow", + // txFunc: func() *txs.Tx { + // input0 := input + // input0.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // input1 := input + // input1.UTXOID.OutputIndex++ + // input1.In = &secp256k1fx.TransferInput{ + // Amt: math.MaxUint64, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input0, + // &input1, + // } + // avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "output overflow", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: math.MaxUint64, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output, + // } + // avax.SortTransferableOutputs(outputs, codec) + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.ImportedIns = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, { name: "invalid credential", txFunc: func() *txs.Tx { @@ -1856,46 +1853,46 @@ func TestSyntacticVerifierImportTx(t *testing.T) { }, err: errWrongNumberOfCredentials, }, - { - name: "barely sufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee, - Input: inputSigners, - } - - tx := tx - tx.ImportedIns = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: nil, - }, - { - name: "barely insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee - 1, - Input: inputSigners, - } - - tx := tx - tx.ImportedIns = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "barely sufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee, + // Input: inputSigners, + // } + + // tx := tx + // tx.ImportedIns = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: nil, + // }, + // { + // name: "barely insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee - 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.ImportedIns = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -2053,201 +2050,201 @@ func TestSyntacticVerifierExportTx(t *testing.T) { }, err: avax.ErrMemoTooLarge, }, - { - name: "invalid output", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: 0, - OutputOwners: outputOwners, - } - - tx := tx - tx.Outs = []*avax.TransferableOutput{ - &output, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueOutput, - }, - { - name: "unsorted outputs", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrOutputsNotSorted, - }, - { - name: "unsorted exported outputs", - txFunc: func() *txs.Tx { - output0 := output - output0.Out = &secp256k1fx.TransferOutput{ - Amt: 1, - OutputOwners: outputOwners, - } - - output1 := output - output1.Out = &secp256k1fx.TransferOutput{ - Amt: 2, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output0, - &output1, - } - avax.SortTransferableOutputs(outputs, codec) - outputs[0], outputs[1] = outputs[1], outputs[0] - - tx := tx - tx.ExportedOuts = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrOutputsNotSorted, - }, - { - name: "invalid input", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 0, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: secp256k1fx.ErrNoValueInput, - }, - { - name: "duplicate inputs", - txFunc: func() *txs.Tx { - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: avax.ErrInputsNotSortedUnique, - }, - { - name: "input overflow", - txFunc: func() *txs.Tx { - input0 := input - input0.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - input1 := input - input1.UTXOID.OutputIndex++ - input1.In = &secp256k1fx.TransferInput{ - Amt: math.MaxUint64, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input0, - &input1, - } - avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) - return &txs.Tx{ - Unsigned: &tx, - Creds: []*fxs.FxCredential{ - &cred, - &cred, - }, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "output overflow", - txFunc: func() *txs.Tx { - output := output - output.Out = &secp256k1fx.TransferOutput{ - Amt: math.MaxUint64, - OutputOwners: outputOwners, - } - - outputs := []*avax.TransferableOutput{ - &output, - } - avax.SortTransferableOutputs(outputs, codec) - - tx := tx - tx.Outs = outputs - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: safemath.ErrOverflow, - }, - { - name: "insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "invalid output", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: 0, + // OutputOwners: outputOwners, + // } + + // tx := tx + // tx.Outs = []*avax.TransferableOutput{ + // &output, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueOutput, + // }, + // { + // name: "unsorted outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "unsorted exported outputs", + // txFunc: func() *txs.Tx { + // output0 := output + // output0.Out = &secp256k1fx.TransferOutput{ + // Amt: 1, + // OutputOwners: outputOwners, + // } + + // output1 := output + // output1.Out = &secp256k1fx.TransferOutput{ + // Amt: 2, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output0, + // &output1, + // } + // avax.SortTransferableOutputs(outputs, codec) + // outputs[0], outputs[1] = outputs[1], outputs[0] + + // tx := tx + // tx.ExportedOuts = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrOutputsNotSorted, + // }, + // { + // name: "invalid input", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 0, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: secp256k1fx.ErrNoValueInput, + // }, + // { + // name: "duplicate inputs", + // txFunc: func() *txs.Tx { + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: avax.ErrInputsNotSortedUnique, + // }, + // { + // name: "input overflow", + // txFunc: func() *txs.Tx { + // input0 := input + // input0.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // input1 := input + // input1.UTXOID.OutputIndex++ + // input1.In = &secp256k1fx.TransferInput{ + // Amt: math.MaxUint64, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input0, + // &input1, + // } + // avax.SortTransferableInputsWithSigners(tx.Ins, make([][]*secp256k1.PrivateKey, 2)) + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: []*fxs.FxCredential{ + // &cred, + // &cred, + // }, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "output overflow", + // txFunc: func() *txs.Tx { + // output := output + // output.Out = &secp256k1fx.TransferOutput{ + // Amt: math.MaxUint64, + // OutputOwners: outputOwners, + // } + + // outputs := []*avax.TransferableOutput{ + // &output, + // } + // avax.SortTransferableOutputs(outputs, codec) + + // tx := tx + // tx.Outs = outputs + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: safemath.ErrOverflow, + // }, + // { + // name: "insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, { name: "invalid credential", txFunc: func() *txs.Tx { @@ -2269,46 +2266,46 @@ func TestSyntacticVerifierExportTx(t *testing.T) { }, err: errWrongNumberOfCredentials, }, - { - name: "barely sufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: nil, - }, - { - name: "barely insufficient funds", - txFunc: func() *txs.Tx { - input := input - input.In = &secp256k1fx.TransferInput{ - Amt: fxOutput.Amt + feeConfig.TxFee - 1, - Input: inputSigners, - } - - tx := tx - tx.Ins = []*avax.TransferableInput{ - &input, - } - return &txs.Tx{ - Unsigned: &tx, - Creds: creds, - } - }, - err: avax.ErrInsufficientFunds, - }, + // { + // name: "barely sufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: nil, + // }, + // { + // name: "barely insufficient funds", + // txFunc: func() *txs.Tx { + // input := input + // input.In = &secp256k1fx.TransferInput{ + // Amt: fxOutput.Amt + feeConfig.TxFee - 1, + // Input: inputSigners, + // } + + // tx := tx + // tx.Ins = []*avax.TransferableInput{ + // &input, + // } + // return &txs.Tx{ + // Unsigned: &tx, + // Creds: creds, + // } + // }, + // err: avax.ErrInsufficientFunds, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/vms/avm/txs/fees/calculator.go b/vms/avm/txs/fees/calculator.go new file mode 100644 index 00000000000..5bcf9d77099 --- /dev/null +++ b/vms/avm/txs/fees/calculator.go @@ -0,0 +1,44 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package fees + +import ( + "github.com/ava-labs/avalanchego/vms/avm/config" + "github.com/ava-labs/avalanchego/vms/avm/txs" +) + +var _ txs.Visitor = (*Calculator)(nil) + +type Calculator struct { + // Pre E-fork inputs + Config *config.Config + + // outputs of visitor execution + Fee uint64 +} + +func (fc *Calculator) BaseTx(*txs.BaseTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) CreateAssetTx(*txs.CreateAssetTx) error { + fc.Fee = fc.Config.CreateAssetTxFee + return nil +} + +func (fc *Calculator) OperationTx(*txs.OperationTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) ImportTx(*txs.ImportTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) ExportTx(*txs.ExportTx) error { + fc.Fee = fc.Config.TxFee + return nil +} diff --git a/vms/avm/vm.go b/vms/avm/vm.go index 2b357708281..4926a0679f3 100644 --- a/vms/avm/vm.go +++ b/vms/avm/vm.go @@ -490,8 +490,9 @@ func (vm *VM) ParseTx(_ context.Context, bytes []byte) (snowstorm.Tx, error) { } err = tx.Unsigned.Visit(&txexecutor.SyntacticVerifier{ - Backend: vm.txBackend, - Tx: tx, + Backend: vm.txBackend, + BlkTimestamp: vm.state.GetTimestamp(), + Tx: tx, }) if err != nil { return nil, err diff --git a/vms/platformvm/txs/executor/import_test.go b/vms/platformvm/txs/executor/import_test.go index fed09bd882c..d513de619d3 100644 --- a/vms/platformvm/txs/executor/import_test.go +++ b/vms/platformvm/txs/executor/import_test.go @@ -137,7 +137,7 @@ func TestNewImportTx(t *testing.T) { }, ), sourceKeys: []*secp256k1.PrivateKey{sourceKey}, - timestamp: env.config.BanffTime, + timestamp: env.config.ApricotPhase5Time, expectedErr: nil, }, } diff --git a/vms/platformvm/txs/executor/staker_tx_verification.go b/vms/platformvm/txs/executor/staker_tx_verification.go index ea9343f1105..092333fb9dc 100644 --- a/vms/platformvm/txs/executor/staker_tx_verification.go +++ b/vms/platformvm/txs/executor/staker_tx_verification.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/avax" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/fees" safemath "github.com/ava-labs/avalanchego/utils/math" ) @@ -164,6 +165,14 @@ func verifyAddValidatorTx( } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return nil, err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -171,7 +180,7 @@ func verifyAddValidatorTx( outs, sTx.Creds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: backend.Config.AddPrimaryNetworkValidatorFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return nil, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -254,6 +263,14 @@ func verifyAddSubnetValidatorTx( } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -261,7 +278,7 @@ func verifyAddSubnetValidatorTx( tx.Outs, baseTxCreds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: backend.Config.AddSubnetValidatorFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -331,6 +348,14 @@ func verifyRemoveSubnetValidatorTx( } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: chainState.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return nil, false, err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -338,7 +363,7 @@ func verifyRemoveSubnetValidatorTx( tx.Outs, baseTxCreds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: backend.Config.TxFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return nil, false, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -446,6 +471,14 @@ func verifyAddDelegatorTx( } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: chainState.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return nil, err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -453,7 +486,7 @@ func verifyAddDelegatorTx( outs, sTx.Creds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: backend.Config.AddPrimaryNetworkDelegatorFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return nil, fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -554,15 +587,10 @@ func verifyAddPermissionlessValidatorTx( ) } - var txFee uint64 if tx.Subnet != constants.PrimaryNetworkID { if err := verifySubnetValidatorPrimaryNetworkRequirements(isDurangoActive, chainState, tx.Validator); err != nil { return err } - - txFee = backend.Config.AddSubnetValidatorFee - } else { - txFee = backend.Config.AddPrimaryNetworkValidatorFee } outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.StakeOuts)) @@ -570,6 +598,14 @@ func verifyAddPermissionlessValidatorTx( copy(outs[len(tx.Outs):], tx.StakeOuts) // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -577,7 +613,7 @@ func verifyAddPermissionlessValidatorTx( outs, sTx.Creds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: txFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -701,7 +737,6 @@ func verifyAddPermissionlessDelegatorTx( copy(outs, tx.Outs) copy(outs[len(tx.Outs):], tx.StakeOuts) - var txFee uint64 if tx.Subnet != constants.PrimaryNetworkID { // Invariant: Delegators must only be able to reference validator // transactions that implement [txs.ValidatorTx]. All @@ -712,10 +747,15 @@ func verifyAddPermissionlessDelegatorTx( if validator.Priority.IsPermissionedValidator() { return ErrDelegateToPermissionedValidator } + } - txFee = backend.Config.AddSubnetDelegatorFee - } else { - txFee = backend.Config.AddPrimaryNetworkDelegatorFee + // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err } // Verify the flowcheck @@ -726,7 +766,7 @@ func verifyAddPermissionlessDelegatorTx( outs, sTx.Creds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: txFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) @@ -772,6 +812,14 @@ func verifyTransferSubnetOwnershipTx( } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: backend.Config, + ChainTime: chainState.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := backend.FlowChecker.VerifySpend( tx, chainState, @@ -779,7 +827,7 @@ func verifyTransferSubnetOwnershipTx( tx.Outs, baseTxCreds, map[ids.ID]uint64{ - backend.Ctx.AVAXAssetID: backend.Config.TxFee, + backend.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return fmt.Errorf("%w: %w", ErrFlowCheckFailed, err) diff --git a/vms/platformvm/txs/executor/standard_tx_executor.go b/vms/platformvm/txs/executor/standard_tx_executor.go index aa3ea9a2aaf..29220ffee0b 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor.go +++ b/vms/platformvm/txs/executor/standard_tx_executor.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/vms/components/verify" "github.com/ava-labs/avalanchego/vms/platformvm/state" "github.com/ava-labs/avalanchego/vms/platformvm/txs" + "github.com/ava-labs/avalanchego/vms/platformvm/txs/fees" ) var ( @@ -68,7 +69,14 @@ func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error { } // Verify the flowcheck - createBlockchainTxFee := e.Config.GetCreateBlockchainTxFee(currentTimestamp) + feeCalculator := fees.Calculator{ + Config: e.Backend.Config, + ChainTime: e.State.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := e.FlowChecker.VerifySpend( tx, e.State, @@ -76,7 +84,7 @@ func (e *StandardTxExecutor) CreateChainTx(tx *txs.CreateChainTx) error { tx.Outs, baseTxCreds, map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: createBlockchainTxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return err @@ -114,7 +122,14 @@ func (e *StandardTxExecutor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { } // Verify the flowcheck - createSubnetTxFee := e.Config.GetCreateSubnetTxFee(currentTimestamp) + feeCalculator := fees.Calculator{ + Config: e.Backend.Config, + ChainTime: e.State.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := e.FlowChecker.VerifySpend( tx, e.State, @@ -122,7 +137,7 @@ func (e *StandardTxExecutor) CreateSubnetTx(tx *txs.CreateSubnetTx) error { tx.Outs, e.Tx.Creds, map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: createSubnetTxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return err @@ -194,6 +209,15 @@ func (e *StandardTxExecutor) ImportTx(tx *txs.ImportTx) error { copy(ins, tx.Ins) copy(ins[len(tx.Ins):], tx.ImportedInputs) + // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: e.Backend.Config, + ChainTime: e.State.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := e.FlowChecker.VerifySpendUTXOs( tx, utxos, @@ -201,7 +225,7 @@ func (e *StandardTxExecutor) ImportTx(tx *txs.ImportTx) error { tx.Outs, e.Tx.Creds, map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.TxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return err @@ -250,6 +274,14 @@ func (e *StandardTxExecutor) ExportTx(tx *txs.ExportTx) error { } // Verify the flowcheck + feeCalculator := fees.Calculator{ + Config: e.Backend.Config, + ChainTime: e.State.GetTimestamp(), + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := e.FlowChecker.VerifySpend( tx, e.State, @@ -257,7 +289,7 @@ func (e *StandardTxExecutor) ExportTx(tx *txs.ExportTx) error { outs, e.Tx.Creds, map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.TxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return fmt.Errorf("failed verifySpend: %w", err) @@ -436,6 +468,13 @@ func (e *StandardTxExecutor) TransformSubnetTx(tx *txs.TransformSubnetTx) error } totalRewardAmount := tx.MaximumSupply - tx.InitialSupply + feeCalculator := fees.Calculator{ + Config: e.Backend.Config, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } if err := e.Backend.FlowChecker.VerifySpend( tx, e.State, @@ -446,7 +485,7 @@ func (e *StandardTxExecutor) TransformSubnetTx(tx *txs.TransformSubnetTx) error // entry in this map literal from being overwritten by the // second entry. map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.TransformSubnetTxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, tx.AssetID: totalRewardAmount, }, ); err != nil { @@ -555,6 +594,18 @@ func (e *StandardTxExecutor) BaseTx(tx *txs.BaseTx) error { } // Verify the flowcheck + var ( + cfg = e.Backend.Config + currentTimestamp = e.State.GetTimestamp() + ) + feeCalculator := fees.Calculator{ + Config: cfg, + ChainTime: currentTimestamp, + } + if err := tx.Visit(&feeCalculator); err != nil { + return err + } + if err := e.FlowChecker.VerifySpend( tx, e.State, @@ -562,7 +613,7 @@ func (e *StandardTxExecutor) BaseTx(tx *txs.BaseTx) error { tx.Outs, e.Tx.Creds, map[ids.ID]uint64{ - e.Ctx.AVAXAssetID: e.Config.TxFee, + e.Ctx.AVAXAssetID: feeCalculator.Fee, }, ); err != nil { return err diff --git a/vms/platformvm/txs/executor/standard_tx_executor_test.go b/vms/platformvm/txs/executor/standard_tx_executor_test.go index 6fd7fd0bc1f..eacd9ac57dc 100644 --- a/vms/platformvm/txs/executor/standard_tx_executor_test.go +++ b/vms/platformvm/txs/executor/standard_tx_executor_test.go @@ -1735,7 +1735,7 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) // Set dependency expectations. - env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) + env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).Times(2) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil).Times(1) subnetOwner := fx.NewMockOwner(ctrl) env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil).Times(1) @@ -1746,14 +1746,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env.state.EXPECT().DeleteCurrentValidator(env.staker) env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1774,14 +1776,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { // Setting the subnet ID to the Primary Network ID makes the tx fail syntactic verification env.tx.Unsigned.(*txs.RemoveSubnetValidatorTx).Subnet = constants.PrimaryNetworkID env.state = state.NewMockDiff(ctrl) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1803,14 +1807,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) env.state.EXPECT().GetPendingValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(nil, database.ErrNotFound) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1835,14 +1841,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { // Set dependency expectations. env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(&staker, nil).Times(1) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1865,14 +1873,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env.state = state.NewMockDiff(ctrl) env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1894,14 +1904,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(nil, database.ErrNotFound) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1925,14 +1937,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { subnetOwner := fx.NewMockOwner(ctrl) env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) env.fx.EXPECT().VerifyPermission(gomock.Any(), env.unsignedTx.SubnetAuth, env.tx.Creds[len(env.tx.Creds)-1], subnetOwner).Return(errTest) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -1951,7 +1965,7 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { newExecutor: func(ctrl *gomock.Controller) (*txs.RemoveSubnetValidatorTx, *StandardTxExecutor) { env := newValidRemoveSubnetValidatorTxVerifyEnv(t, ctrl) env.state = state.NewMockDiff(ctrl) - env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) + env.state.EXPECT().GetTimestamp().Return(env.latestForkTime).Times(2) env.state.EXPECT().GetCurrentValidator(env.unsignedTx.Subnet, env.unsignedTx.NodeID).Return(env.staker, nil) subnetOwner := fx.NewMockOwner(ctrl) env.state.EXPECT().GetSubnetOwner(env.unsignedTx.Subnet).Return(subnetOwner, nil) @@ -1959,14 +1973,16 @@ func TestStandardExecutorRemoveSubnetValidatorTx(t *testing.T) { env.flowChecker.EXPECT().VerifySpend( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(errTest) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -2117,14 +2133,16 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { // Setting the tx to nil makes the tx fail syntactic verification env.tx.Unsigned = (*txs.TransformSubnetTx)(nil) env.state = state.NewMockDiff(ctrl) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -2144,15 +2162,17 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { env := newValidTransformSubnetTxVerifyEnv(t, ctrl) env.unsignedTx.MaxStakeDuration = math.MaxUint32 env.state = state.NewMockDiff(ctrl) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + } env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -2173,16 +2193,18 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { // Remove credentials env.tx.Creds = nil env.state = state.NewMockDiff(ctrl) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + MaxStakeDuration: math.MaxInt64, + } env.state.EXPECT().GetTimestamp().Return(env.latestForkTime) e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - MaxStakeDuration: math.MaxInt64, - EUpgradeTime: mockable.MaxTime, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -2209,15 +2231,17 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { env.flowChecker.EXPECT().VerifySpend( gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), ).Return(ErrFlowCheckFailed) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + MaxStakeDuration: math.MaxInt64, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - MaxStakeDuration: math.MaxInt64, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, @@ -2249,15 +2273,17 @@ func TestStandardExecutorTransformSubnetTx(t *testing.T) { env.state.EXPECT().SetCurrentSupply(env.unsignedTx.Subnet, env.unsignedTx.InitialSupply) env.state.EXPECT().DeleteUTXO(gomock.Any()).Times(len(env.unsignedTx.Ins)) env.state.EXPECT().AddUTXO(gomock.Any()).Times(len(env.unsignedTx.Outs)) + + cfg := &config.Config{ + BanffTime: env.latestForkTime, + CortinaTime: env.latestForkTime, + DurangoTime: env.latestForkTime, + EUpgradeTime: mockable.MaxTime, + MaxStakeDuration: math.MaxInt64, + } e := &StandardTxExecutor{ Backend: &Backend{ - Config: &config.Config{ - BanffTime: env.latestForkTime, - CortinaTime: env.latestForkTime, - DurangoTime: env.latestForkTime, - EUpgradeTime: mockable.MaxTime, - MaxStakeDuration: math.MaxInt64, - }, + Config: cfg, Bootstrapped: &utils.Atomic[bool]{}, Fx: env.fx, FlowChecker: env.flowChecker, diff --git a/vms/platformvm/txs/fees/calculator.go b/vms/platformvm/txs/fees/calculator.go new file mode 100644 index 00000000000..123052577e5 --- /dev/null +++ b/vms/platformvm/txs/fees/calculator.go @@ -0,0 +1,104 @@ +// Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package fees + +import ( + "time" + + "github.com/ava-labs/avalanchego/utils/constants" + "github.com/ava-labs/avalanchego/vms/platformvm/config" + "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +var _ txs.Visitor = (*Calculator)(nil) + +type Calculator struct { + // Pre E-fork inputs + Config *config.Config + ChainTime time.Time + + // outputs of visitor execution + Fee uint64 +} + +func (fc *Calculator) AddValidatorTx(*txs.AddValidatorTx) error { + fc.Fee = fc.Config.AddPrimaryNetworkValidatorFee + return nil +} + +func (fc *Calculator) AddSubnetValidatorTx(*txs.AddSubnetValidatorTx) error { + fc.Fee = fc.Config.AddSubnetValidatorFee + return nil +} + +func (fc *Calculator) AddDelegatorTx(*txs.AddDelegatorTx) error { + fc.Fee = fc.Config.AddPrimaryNetworkDelegatorFee + return nil +} + +func (fc *Calculator) CreateChainTx(*txs.CreateChainTx) error { + fc.Fee = fc.Config.GetCreateBlockchainTxFee(fc.ChainTime) + return nil +} + +func (fc *Calculator) CreateSubnetTx(*txs.CreateSubnetTx) error { + fc.Fee = fc.Config.GetCreateSubnetTxFee(fc.ChainTime) + return nil +} + +func (*Calculator) AdvanceTimeTx(*txs.AdvanceTimeTx) error { + return nil // no fees +} + +func (*Calculator) RewardValidatorTx(*txs.RewardValidatorTx) error { + return nil // no fees +} + +func (fc *Calculator) RemoveSubnetValidatorTx(*txs.RemoveSubnetValidatorTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) TransformSubnetTx(*txs.TransformSubnetTx) error { + fc.Fee = fc.Config.TransformSubnetTxFee + return nil +} + +func (fc *Calculator) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) AddPermissionlessValidatorTx(tx *txs.AddPermissionlessValidatorTx) error { + if tx.Subnet != constants.PrimaryNetworkID { + fc.Fee = fc.Config.AddSubnetValidatorFee + } else { + fc.Fee = fc.Config.AddPrimaryNetworkValidatorFee + } + return nil +} + +func (fc *Calculator) AddPermissionlessDelegatorTx(tx *txs.AddPermissionlessDelegatorTx) error { + if tx.Subnet != constants.PrimaryNetworkID { + fc.Fee = fc.Config.AddSubnetDelegatorFee + } else { + fc.Fee = fc.Config.AddPrimaryNetworkDelegatorFee + } + return nil +} + +func (fc *Calculator) BaseTx(*txs.BaseTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) ImportTx(*txs.ImportTx) error { + fc.Fee = fc.Config.TxFee + return nil +} + +func (fc *Calculator) ExportTx(*txs.ExportTx) error { + fc.Fee = fc.Config.TxFee + return nil +}