Ori Newman dbababb978
Limit mempool size to million transactions and remove the least profitable transactions (#1483)
* Limit mempool size to million transactions and remove the least profitable transactions

* Simplify insert

* Fix typo

* Improve findTxIndexInOrderedTransactionsByFeeRate readability
2021-02-03 19:45:39 +02:00

337 lines
9.3 KiB
Go

package externalapi
import (
"bytes"
"fmt"
"github.com/pkg/errors"
)
// DomainTransaction represents a Kaspa transaction
type DomainTransaction struct {
Version uint16
Inputs []*DomainTransactionInput
Outputs []*DomainTransactionOutput
LockTime uint64
SubnetworkID DomainSubnetworkID
Gas uint64
PayloadHash DomainHash
Payload []byte
Fee uint64
Mass uint64
// ID is a field that is used to cache the transaction ID.
// Always use consensushashing.TransactionID instead of accessing this field directly
ID *DomainTransactionID
}
// Clone returns a clone of DomainTransaction
func (tx *DomainTransaction) Clone() *DomainTransaction {
payloadClone := make([]byte, len(tx.Payload))
copy(payloadClone, tx.Payload)
inputsClone := make([]*DomainTransactionInput, len(tx.Inputs))
for i, input := range tx.Inputs {
inputsClone[i] = input.Clone()
}
outputsClone := make([]*DomainTransactionOutput, len(tx.Outputs))
for i, output := range tx.Outputs {
outputsClone[i] = output.Clone()
}
var idClone *DomainTransactionID
if tx.ID != nil {
idClone = tx.ID.Clone()
}
return &DomainTransaction{
Version: tx.Version,
Inputs: inputsClone,
Outputs: outputsClone,
LockTime: tx.LockTime,
SubnetworkID: *tx.SubnetworkID.Clone(),
Gas: tx.Gas,
PayloadHash: tx.PayloadHash,
Payload: payloadClone,
Fee: tx.Fee,
Mass: tx.Mass,
ID: idClone,
}
}
// If this doesn't compile, it means the type definition has been changed, so it's
// an indication to update Equal and Clone accordingly.
var _ = DomainTransaction{0, []*DomainTransactionInput{}, []*DomainTransactionOutput{}, 0,
DomainSubnetworkID{}, 0, DomainHash{}, []byte{}, 0, 0,
&DomainTransactionID{}}
// Equal returns whether tx equals to other
func (tx *DomainTransaction) Equal(other *DomainTransaction) bool {
if tx == nil || other == nil {
return tx == other
}
if tx.Version != other.Version {
return false
}
if len(tx.Inputs) != len(other.Inputs) {
return false
}
for i, input := range tx.Inputs {
if !input.Equal(other.Inputs[i]) {
return false
}
}
if len(tx.Outputs) != len(other.Outputs) {
return false
}
for i, output := range tx.Outputs {
if !output.Equal(other.Outputs[i]) {
return false
}
}
if tx.LockTime != other.LockTime {
return false
}
if !tx.SubnetworkID.Equal(&other.SubnetworkID) {
return false
}
if tx.Gas != other.Gas {
return false
}
if !tx.PayloadHash.Equal(&other.PayloadHash) {
return false
}
if !bytes.Equal(tx.Payload, other.Payload) {
return false
}
if tx.Fee != 0 && other.Fee != 0 && tx.Fee != other.Fee {
panic(errors.New("identical transactions should always have the same fee"))
}
if tx.Mass != 0 && other.Mass != 0 && tx.Mass != other.Mass {
panic(errors.New("identical transactions should always have the same mass"))
}
if tx.ID != nil && other.ID != nil && !tx.ID.Equal(other.ID) {
panic(errors.New("identical transactions should always have the same ID"))
}
return true
}
// DomainTransactionInput represents a Kaspa transaction input
type DomainTransactionInput struct {
PreviousOutpoint DomainOutpoint
SignatureScript []byte
Sequence uint64
UTXOEntry UTXOEntry
}
// If this doesn't compile, it means the type definition has been changed, so it's
// an indication to update Equal and Clone accordingly.
var _ = &DomainTransactionInput{DomainOutpoint{}, []byte{}, 0, nil}
// Equal returns whether input equals to other
func (input *DomainTransactionInput) Equal(other *DomainTransactionInput) bool {
if input == nil || other == nil {
return input == other
}
if !input.PreviousOutpoint.Equal(&other.PreviousOutpoint) {
return false
}
if !bytes.Equal(input.SignatureScript, other.SignatureScript) {
return false
}
if input.Sequence != other.Sequence {
return false
}
if input.UTXOEntry != nil && other.UTXOEntry != nil && !input.UTXOEntry.Equal(other.UTXOEntry) {
panic(errors.New("identical inputs should always have the same UTXO entry"))
}
return true
}
// Clone returns a clone of DomainTransactionInput
func (input *DomainTransactionInput) Clone() *DomainTransactionInput {
signatureScriptClone := make([]byte, len(input.SignatureScript))
copy(signatureScriptClone, input.SignatureScript)
return &DomainTransactionInput{
PreviousOutpoint: *input.PreviousOutpoint.Clone(),
SignatureScript: signatureScriptClone,
Sequence: input.Sequence,
UTXOEntry: input.UTXOEntry,
}
}
// DomainOutpoint represents a Kaspa transaction outpoint
type DomainOutpoint struct {
TransactionID DomainTransactionID
Index uint32
}
// If this doesn't compile, it means the type definition has been changed, so it's
// an indication to update Equal and Clone accordingly.
var _ = DomainOutpoint{DomainTransactionID{}, 0}
// Equal returns whether op equals to other
func (op *DomainOutpoint) Equal(other *DomainOutpoint) bool {
if op == nil || other == nil {
return op == other
}
return *op == *other
}
// Clone returns a clone of DomainOutpoint
func (op *DomainOutpoint) Clone() *DomainOutpoint {
return &DomainOutpoint{
TransactionID: *op.TransactionID.Clone(),
Index: op.Index,
}
}
// String stringifies an outpoint.
func (op DomainOutpoint) String() string {
return fmt.Sprintf("(%s: %d)", op.TransactionID, op.Index)
}
// NewDomainOutpoint instantiates a new DomainOutpoint with the given id and index
func NewDomainOutpoint(id *DomainTransactionID, index uint32) *DomainOutpoint {
return &DomainOutpoint{
TransactionID: *id,
Index: index,
}
}
// ScriptPublicKey represents a Kaspad ScriptPublicKey
type ScriptPublicKey struct {
Script []byte
Version uint16
}
// DomainTransactionOutput represents a Kaspad transaction output
type DomainTransactionOutput struct {
Value uint64
ScriptPublicKey *ScriptPublicKey
}
// If this doesn't compile, it means the type definition has been changed, so it's
// an indication to update Equal and Clone accordingly.
var _ = DomainTransactionOutput{0, &ScriptPublicKey{Script: []byte{}, Version: 0}}
// Equal returns whether output equals to other
func (output *DomainTransactionOutput) Equal(other *DomainTransactionOutput) bool {
if output == nil || other == nil {
return output == other
}
if output.Value != other.Value {
return false
}
if !bytes.Equal(output.ScriptPublicKey.Script, other.ScriptPublicKey.Script) {
return false
}
return true
}
// Clone returns a clone of DomainTransactionOutput
func (output *DomainTransactionOutput) Clone() *DomainTransactionOutput {
scriptPublicKeyClone := &ScriptPublicKey{
Script: make([]byte, len(output.ScriptPublicKey.Script)),
Version: output.ScriptPublicKey.Version}
copy(scriptPublicKeyClone.Script, output.ScriptPublicKey.Script)
return &DomainTransactionOutput{
Value: output.Value,
ScriptPublicKey: scriptPublicKeyClone,
}
}
// DomainTransactionID represents the ID of a Kaspa transaction
type DomainTransactionID DomainHash
// NewDomainTransactionIDFromByteArray constructs a new TransactionID out of a byte array
func NewDomainTransactionIDFromByteArray(transactionIDBytes *[DomainHashSize]byte) *DomainTransactionID {
return (*DomainTransactionID)(NewDomainHashFromByteArray(transactionIDBytes))
}
// NewDomainTransactionIDFromByteSlice constructs a new TransactionID out of a byte slice
// Returns an error if the length of the byte slice is not exactly `DomainHashSize`
func NewDomainTransactionIDFromByteSlice(transactionIDBytes []byte) (*DomainTransactionID, error) {
hash, err := NewDomainHashFromByteSlice(transactionIDBytes)
if err != nil {
return nil, err
}
return (*DomainTransactionID)(hash), nil
}
// NewDomainTransactionIDFromString constructs a new TransactionID out of a string
// Returns an error if the length of the string is not exactly `DomainHashSize * 2`
func NewDomainTransactionIDFromString(transactionIDString string) (*DomainTransactionID, error) {
hash, err := NewDomainHashFromString(transactionIDString)
if err != nil {
return nil, err
}
return (*DomainTransactionID)(hash), nil
}
// String stringifies a transaction ID.
func (id DomainTransactionID) String() string {
return DomainHash(id).String()
}
// Clone returns a clone of DomainTransactionID
func (id *DomainTransactionID) Clone() *DomainTransactionID {
idClone := *id
return &idClone
}
// Equal returns whether id equals to other
func (id *DomainTransactionID) Equal(other *DomainTransactionID) bool {
return (*DomainHash)(id).Equal((*DomainHash)(other))
}
// Less returns true if id is less than other
func (id *DomainTransactionID) Less(other *DomainTransactionID) bool {
return (*DomainHash)(id).Less((*DomainHash)(other))
}
// LessOrEqual returns true if id is smaller or equal to other
func (id *DomainTransactionID) LessOrEqual(other *DomainTransactionID) bool {
return (*DomainHash)(id).LessOrEqual((*DomainHash)(other))
}
// ByteArray returns the bytes in this transactionID represented as a byte array.
// The transactionID bytes are cloned, therefore it is safe to modify the resulting array.
func (id *DomainTransactionID) ByteArray() *[DomainHashSize]byte {
return (*DomainHash)(id).ByteArray()
}
// ByteSlice returns the bytes in this transactionID represented as a byte slice.
// The transactionID bytes are cloned, therefore it is safe to modify the resulting slice.
func (id *DomainTransactionID) ByteSlice() []byte {
return (*DomainHash)(id).ByteSlice()
}