mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
202 lines
7.5 KiB
Go
202 lines
7.5 KiB
Go
package mempoollimits
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/kaspanet/go-secp256k1"
|
|
"github.com/kaspanet/kaspad/app/appmessage"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/constants"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/transactionhelper"
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
|
|
utxopkg "github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
|
"github.com/kaspanet/kaspad/stability-tests/common/mine"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
const (
|
|
payAddress = "kaspasim:qzuax2jhawd354e54thhpd9m9wg03pdzwjlpr4vtq3k7xrpumhhtwa2hkr3ep"
|
|
payAddressPrivateKey = "05d8f681e954a550395ee2297fc1a14f6e801f554c0b9d48cd7165a7ea72ff77"
|
|
fundingCoinbaseTransactionAmount = 1000
|
|
outputsPerTransaction = 3
|
|
transactionFee = 1000
|
|
coinbaseMaturity = 100
|
|
)
|
|
|
|
// fundingCoinbaseTransactions contains a collection of transactions
|
|
// to be utilized when generating further transactions to fill up
|
|
// the mempool.
|
|
// It's a separate type because we modify the transactions in place
|
|
// whenever we pass an instance of this type into
|
|
// submitAnAmountOfTransactionsToTheMempool.
|
|
type fundingCoinbaseTransactions struct {
|
|
transactions []*externalapi.DomainTransaction
|
|
}
|
|
|
|
func generateFundingCoinbaseTransactions(t *testing.T, rpcClient *rpcclient.RPCClient) *fundingCoinbaseTransactions {
|
|
// Mine a block, since we need at least one block above the genesis
|
|
// to create a spendable UTXO
|
|
mineBlockAndGetCoinbaseTransaction(t, rpcClient)
|
|
|
|
log.Infof("Generating funding coinbase transactions")
|
|
fundingCoinbaseTransactions := &fundingCoinbaseTransactions{
|
|
transactions: make([]*externalapi.DomainTransaction, fundingCoinbaseTransactionAmount),
|
|
}
|
|
for i := 0; i < fundingCoinbaseTransactionAmount; i++ {
|
|
fundingCoinbaseTransactions.transactions[i] = mineBlockAndGetCoinbaseTransaction(t, rpcClient)
|
|
}
|
|
|
|
log.Infof("Maturing funding coinbase transactions")
|
|
for i := 0; i < coinbaseMaturity; i++ {
|
|
mineBlockAndGetCoinbaseTransaction(t, rpcClient)
|
|
}
|
|
|
|
return fundingCoinbaseTransactions
|
|
}
|
|
|
|
func submitAnAmountOfTransactionsToTheMempool(t *testing.T, rpcClient *rpcclient.RPCClient,
|
|
payAddressKeyPair *secp256k1.SchnorrKeyPair, payToPayAddressScript *externalapi.ScriptPublicKey,
|
|
fundingTransactions *fundingCoinbaseTransactions, amountToSubmit int, ignoreOrphanRejects bool) {
|
|
|
|
log.Infof("Generating %d transactions", amountToSubmit)
|
|
transactions := make([]*externalapi.DomainTransaction, 0)
|
|
for len(transactions) < amountToSubmit {
|
|
var coinbaseTransaction *externalapi.DomainTransaction
|
|
coinbaseTransaction, fundingTransactions.transactions = fundingTransactions.transactions[0], fundingTransactions.transactions[1:]
|
|
|
|
unspentTransactions := []*externalapi.DomainTransaction{coinbaseTransaction}
|
|
for len(transactions) < amountToSubmit && len(unspentTransactions) > 0 {
|
|
var transactionToSpend *externalapi.DomainTransaction
|
|
transactionToSpend, unspentTransactions = unspentTransactions[0], unspentTransactions[1:]
|
|
spendingTransactions := generateTransactionsWithMultipleOutputs(t, payAddressKeyPair, payToPayAddressScript, transactionToSpend)
|
|
transactions = append(transactions, spendingTransactions...)
|
|
unspentTransactions = append(unspentTransactions, spendingTransactions...)
|
|
}
|
|
log.Infof("Generated %d transactions", len(transactions))
|
|
}
|
|
|
|
transactions = transactions[:amountToSubmit]
|
|
log.Infof("Submitting %d transactions", len(transactions))
|
|
|
|
for i, transaction := range transactions {
|
|
rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
|
|
_, err := rpcClient.SubmitTransaction(rpcTransaction, false)
|
|
if err != nil {
|
|
if ignoreOrphanRejects && strings.Contains(err.Error(), "orphan") {
|
|
continue
|
|
}
|
|
t.Fatalf("SubmitTransaction: %+v", err)
|
|
}
|
|
log.Infof("Submitted %d transactions", i+1)
|
|
}
|
|
}
|
|
|
|
func mineBlockAndGetCoinbaseTransaction(t *testing.T, rpcClient *rpcclient.RPCClient) *externalapi.DomainTransaction {
|
|
getBlockTemplateResponse, err := rpcClient.GetBlockTemplate(payAddress, "")
|
|
if err != nil {
|
|
t.Fatalf("GetBlockTemplate: %+v", err)
|
|
}
|
|
templateBlock, err := appmessage.RPCBlockToDomainBlock(getBlockTemplateResponse.Block)
|
|
if err != nil {
|
|
t.Fatalf("RPCBlockToDomainBlock: %+v", err)
|
|
}
|
|
mine.SolveBlock(templateBlock)
|
|
_, err = rpcClient.SubmitBlockAlsoIfNonDAA(templateBlock)
|
|
if err != nil {
|
|
t.Fatalf("SubmitBlock: %+v", err)
|
|
}
|
|
return templateBlock.Transactions[transactionhelper.CoinbaseTransactionIndex]
|
|
}
|
|
|
|
func generateTransactionsWithMultipleOutputs(t *testing.T,
|
|
payAddressKeyPair *secp256k1.SchnorrKeyPair, payToPayAddressScript *externalapi.ScriptPublicKey,
|
|
fundingTransaction *externalapi.DomainTransaction) []*externalapi.DomainTransaction {
|
|
|
|
var transactions []*externalapi.DomainTransaction
|
|
for fundingTransactionOutputIndex, fundingTransactionOutput := range fundingTransaction.Outputs {
|
|
if fundingTransactionOutput.Value < transactionFee {
|
|
continue
|
|
}
|
|
outputValue := (fundingTransactionOutput.Value - transactionFee) / outputsPerTransaction
|
|
|
|
fundingTransactionID := consensushashing.TransactionID(fundingTransaction)
|
|
spendingTransactionInputs := []*externalapi.DomainTransactionInput{
|
|
{
|
|
PreviousOutpoint: externalapi.DomainOutpoint{
|
|
TransactionID: *fundingTransactionID,
|
|
Index: uint32(fundingTransactionOutputIndex),
|
|
},
|
|
UTXOEntry: utxopkg.NewUTXOEntry(
|
|
fundingTransactionOutput.Value,
|
|
payToPayAddressScript,
|
|
false,
|
|
0),
|
|
},
|
|
}
|
|
|
|
spendingTransactionOutputs := make([]*externalapi.DomainTransactionOutput, outputsPerTransaction)
|
|
for i := 0; i < outputsPerTransaction; i++ {
|
|
spendingTransactionOutputs[i] = &externalapi.DomainTransactionOutput{
|
|
Value: outputValue,
|
|
ScriptPublicKey: payToPayAddressScript,
|
|
}
|
|
}
|
|
|
|
spendingTransaction := &externalapi.DomainTransaction{
|
|
Version: constants.MaxTransactionVersion,
|
|
Inputs: spendingTransactionInputs,
|
|
Outputs: spendingTransactionOutputs,
|
|
LockTime: 0,
|
|
SubnetworkID: subnetworks.SubnetworkIDNative,
|
|
Gas: 0,
|
|
Payload: nil,
|
|
}
|
|
|
|
for spendingTransactionInputIndex, spendingTransactionInput := range spendingTransactionInputs {
|
|
signatureScript, err := txscript.SignatureScript(
|
|
spendingTransaction,
|
|
spendingTransactionInputIndex,
|
|
consensushashing.SigHashAll,
|
|
payAddressKeyPair,
|
|
&consensushashing.SighashReusedValues{})
|
|
if err != nil {
|
|
t.Fatalf("SignatureScript: %+v", err)
|
|
}
|
|
spendingTransactionInput.SignatureScript = signatureScript
|
|
}
|
|
|
|
transactions = append(transactions, spendingTransaction)
|
|
}
|
|
return transactions
|
|
}
|
|
|
|
func decodePayAddressKeyPair(t *testing.T) *secp256k1.SchnorrKeyPair {
|
|
privateKeyBytes, err := hex.DecodeString(payAddressPrivateKey)
|
|
if err != nil {
|
|
t.Fatalf("DecodeString: %+v", err)
|
|
}
|
|
keyPair, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKeyBytes)
|
|
if err != nil {
|
|
t.Fatalf("DeserializeSchnorrPrivateKeyFromSlice: %+v", err)
|
|
}
|
|
return keyPair
|
|
}
|
|
|
|
func buildPayToPayAddressScript(t *testing.T) *externalapi.ScriptPublicKey {
|
|
address, err := util.DecodeAddress(payAddress, dagconfig.SimnetParams.Prefix)
|
|
if err != nil {
|
|
t.Fatalf("DecodeAddress: %+v", err)
|
|
}
|
|
script, err := txscript.PayToAddrScript(address)
|
|
if err != nil {
|
|
t.Fatalf("PayToAddrScript: %+v", err)
|
|
}
|
|
return script
|
|
}
|