kaspad/cmd/kaspawallet/daemon/server/split_transaction_test.go
Ori Newman 1e9ddc42d0
Add fee estimation to wallet (#2291)
* Add fee estimation to wallet

* Add fee rate to kaspawallet parse

* Update go version

* Get rid of golint

* Add RBF support to wallet

* Fix bump_fee UTXO lookup and fix wrong change address

* impl storage mass as per KIP9

* Use CalculateTransactionOverallMass where needed

* Some fixes

* Minor typos

* Fix test

* update version

* BroadcastRBF -> BroadcastReplacement

* rc3

* align proto files to only use camel case (fixed on RK as well)

* Rename to FeePolicy and add MaxFee option + todo

* apply max fee constrains

* increase minChangeTarget to 10kas

* fmt

* Some fixes

* fix description: maximum -> minimum

* put min feerate check in the correct location

* Fix calculateFeeLimits nil handling

* Add validations to CLI flags

* Change to rc6

* Add checkTransactionFeeRate

* Add failed broadcast transactions on send error`

* Fix estimateFee change value

* Estimate fee correctly for --send-all

* On estimateFee always assume that the recipient has ECDSA address

* remove patch version

---------

Co-authored-by: Michael Sutton <msutton@cs.huji.ac.il>
2024-10-22 12:34:54 +03:00

212 lines
7.0 KiB
Go

package server
import (
"testing"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/serialization"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/util/txmass"
"github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/kaspanet/kaspad/domain/consensus"
"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
)
func TestEstimateComputeMassAfterSignatures(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
unsignedTransaction, mnemonics, params, teardown := testEstimateMassIncreaseForSignaturesSetUp(t, consensusConfig)
defer teardown(false)
serverInstance := &server{
params: params,
keysFile: &keys.File{MinimumSignatures: 2},
shutdown: make(chan struct{}),
addressSet: make(walletAddressSet),
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
}
estimatedMassAfterSignatures, err := serverInstance.estimateComputeMassAfterSignatures(unsignedTransaction)
if err != nil {
t.Fatalf("Error from estimateComputeMassAfterSignatures: %s", err)
}
unsignedTransactionBytes, err := serialization.SerializePartiallySignedTransaction(unsignedTransaction)
if err != nil {
t.Fatalf("Error deserializing unsignedTransaction: %s", err)
}
signedTxStep1Bytes, err := libkaspawallet.Sign(params, mnemonics[:1], unsignedTransactionBytes, false)
if err != nil {
t.Fatalf("Sign: %+v", err)
}
signedTxStep2Bytes, err := libkaspawallet.Sign(params, mnemonics[1:2], signedTxStep1Bytes, false)
if err != nil {
t.Fatalf("Sign: %+v", err)
}
extractedSignedTx, err := libkaspawallet.ExtractTransaction(signedTxStep2Bytes, false)
if err != nil {
t.Fatalf("ExtractTransaction: %+v", err)
}
actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionMass(extractedSignedTx)
if estimatedMassAfterSignatures != actualMassAfterSignatures {
t.Errorf("Estimated mass after signatures: %d but actually got %d",
estimatedMassAfterSignatures, actualMassAfterSignatures)
}
})
}
func TestEstimateMassAfterSignatures(t *testing.T) {
testutils.ForAllNets(t, true, func(t *testing.T, consensusConfig *consensus.Config) {
unsignedTransaction, mnemonics, params, teardown := testEstimateMassIncreaseForSignaturesSetUp(t, consensusConfig)
defer teardown(false)
for i := range unsignedTransaction.Tx.Inputs {
unsignedTransaction.Tx.Inputs[i].UTXOEntry = utxo.NewUTXOEntry(1, &externalapi.ScriptPublicKey{}, false, 0)
unsignedTransaction.PartiallySignedInputs[i].PrevOutput = &externalapi.DomainTransactionOutput{
Value: 1,
ScriptPublicKey: &externalapi.ScriptPublicKey{},
}
}
serverInstance := &server{
params: params,
keysFile: &keys.File{MinimumSignatures: 2},
shutdown: make(chan struct{}),
addressSet: make(walletAddressSet),
txMassCalculator: txmass.NewCalculator(params.MassPerTxByte, params.MassPerScriptPubKeyByte, params.MassPerSigOp),
}
estimatedMassAfterSignatures, err := serverInstance.estimateMassAfterSignatures(unsignedTransaction)
if err != nil {
t.Fatalf("Error from estimateMassAfterSignatures: %s", err)
}
unsignedTransactionBytes, err := serialization.SerializePartiallySignedTransaction(unsignedTransaction)
if err != nil {
t.Fatalf("Error deserializing unsignedTransaction: %s", err)
}
signedTxStep1Bytes, err := libkaspawallet.Sign(params, mnemonics[:1], unsignedTransactionBytes, false)
if err != nil {
t.Fatalf("Sign: %+v", err)
}
signedTxStep2Bytes, err := libkaspawallet.Sign(params, mnemonics[1:2], signedTxStep1Bytes, false)
if err != nil {
t.Fatalf("Sign: %+v", err)
}
extractedSignedTx, err := libkaspawallet.ExtractTransaction(signedTxStep2Bytes, false)
if err != nil {
t.Fatalf("ExtractTransaction: %+v", err)
}
for i := range extractedSignedTx.Inputs {
extractedSignedTx.Inputs[i].UTXOEntry = utxo.NewUTXOEntry(1, &externalapi.ScriptPublicKey{}, false, 0)
}
actualMassAfterSignatures := serverInstance.txMassCalculator.CalculateTransactionOverallMass(extractedSignedTx)
if estimatedMassAfterSignatures != actualMassAfterSignatures {
t.Errorf("Estimated mass after signatures: %d but actually got %d",
estimatedMassAfterSignatures, actualMassAfterSignatures)
}
})
}
func testEstimateMassIncreaseForSignaturesSetUp(t *testing.T, consensusConfig *consensus.Config) (
*serialization.PartiallySignedTransaction, []string, *dagconfig.Params, func(keepDataDir bool)) {
consensusConfig.BlockCoinbaseMaturity = 0
params := &consensusConfig.Params
tc, teardown, err := consensus.NewFactory().NewTestConsensus(consensusConfig, "TestMultisig")
if err != nil {
t.Fatalf("Error setting up tc: %+v", err)
}
const numKeys = 3
mnemonics := make([]string, numKeys)
publicKeys := make([]string, numKeys)
for i := 0; i < numKeys; i++ {
var err error
mnemonics[i], err = libkaspawallet.CreateMnemonic()
if err != nil {
t.Fatalf("CreateMnemonic: %+v", err)
}
publicKeys[i], err = libkaspawallet.MasterPublicKeyFromMnemonic(&consensusConfig.Params, mnemonics[i], true)
if err != nil {
t.Fatalf("MasterPublicKeyFromMnemonic: %+v", err)
}
}
const minimumSignatures = 2
path := "m/1/2/3"
address, err := libkaspawallet.Address(params, publicKeys, minimumSignatures, path, false)
if err != nil {
t.Fatalf("Address: %+v", err)
}
scriptPublicKey, err := txscript.PayToAddrScript(address)
if err != nil {
t.Fatalf("PayToAddrScript: %+v", err)
}
coinbaseData := &externalapi.DomainCoinbaseData{
ScriptPublicKey: scriptPublicKey,
ExtraData: nil,
}
fundingBlockHash, _, err := tc.AddBlock([]*externalapi.DomainHash{consensusConfig.GenesisHash}, coinbaseData, nil)
if err != nil {
t.Fatalf("AddBlock: %+v", err)
}
block1Hash, _, err := tc.AddBlock([]*externalapi.DomainHash{fundingBlockHash}, nil, nil)
if err != nil {
t.Fatalf("AddBlock: %+v", err)
}
block1, _, err := tc.GetBlock(block1Hash)
if err != nil {
t.Fatalf("GetBlock: %+v", err)
}
block1Tx := block1.Transactions[0]
block1TxOut := block1Tx.Outputs[0]
selectedUTXOs := []*libkaspawallet.UTXO{
{
Outpoint: &externalapi.DomainOutpoint{
TransactionID: *consensushashing.TransactionID(block1.Transactions[0]),
Index: 0,
},
UTXOEntry: utxo.NewUTXOEntry(block1TxOut.Value, block1TxOut.ScriptPublicKey, true, 0),
DerivationPath: path,
},
}
unsignedTransaction, err := libkaspawallet.CreateUnsignedTransaction(publicKeys, minimumSignatures,
[]*libkaspawallet.Payment{{
Address: address,
Amount: 10,
}}, selectedUTXOs)
if err != nil {
t.Fatalf("CreateUnsignedTransactions: %+v", err)
}
return unsignedTransaction, mnemonics, params, teardown
}