mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00

* Update constants * Add to transaction SigOpCount * Update mass calculation, and move it from InContext to InIsolation * Update block validation accordingly * Add SigOpCount validation during TransactionInContext * Remove checking of mass vs maxMassAcceptedByBlock from consensusStateManager * Update mining manager with latest changes * Add SigOpCount to MsgTx.Copy() * Fix initTestTransactionAcceptanceDataForClone * Fix all tests in transaction_equal_clone_test.go * Fix TestBlockMass * Fix tests in transactionvalidator package * Add SigOpCount to sighash * Fix TestPruningDepth * Fix problems in libkaspawalelt * Fix integration tests * Fix CalculateSignatureHash tests * Remove remaining places talking about block size * Add sanity check to checkBlockMass to make sure all transactions have their mass filled * always add own sigOpCount to sigHash * Update protowire/rpc.md * Start working on removing any remaining reference to block/tx size * Update rpc transaction verbose data to include mass rather then size * Convert verboseData and block size check to mass * Remove remaining usages of tx size in mempool * Move transactionEstimatedSerializedSize to transactionvalidator * Add PopulateMass to fakeRelayInvsContext * Move PopulateMass to beggining of ValidateAndInsertTransaction + fix in it * Assign mass a new number for backward-compatibility
320 lines
11 KiB
Go
320 lines
11 KiB
Go
// Copyright (c) 2013-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package appmessage
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"strconv"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/utils/subnetworks"
|
|
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
)
|
|
|
|
const (
|
|
// MaxPrevOutIndex is the maximum index the index field of a previous
|
|
// outpoint can be.
|
|
MaxPrevOutIndex uint32 = 0xffffffff
|
|
|
|
// defaultTxInOutAlloc is the default size used for the backing array for
|
|
// transaction inputs and outputs. The array will dynamically grow as needed,
|
|
// but this figure is intended to provide enough space for the number of
|
|
// inputs and outputs in a typical transaction without needing to grow the
|
|
// backing array multiple times.
|
|
defaultTxInOutAlloc = 15
|
|
|
|
// minTxInPayload is the minimum payload size for a transaction input.
|
|
// PreviousOutpoint.TxID + PreviousOutpoint.Index 4 bytes + Varint for
|
|
// SignatureScript length 1 byte + Sequence 4 bytes.
|
|
minTxInPayload = 9 + externalapi.DomainHashSize
|
|
|
|
// maxTxInPerMessage is the maximum number of transactions inputs that
|
|
// a transaction which fits into a message could possibly have.
|
|
maxTxInPerMessage = (MaxMessagePayload / minTxInPayload) + 1
|
|
|
|
// MinTxOutPayload is the minimum payload size for a transaction output.
|
|
// Value 8 bytes + version 2 bytes + Varint for ScriptPublicKey length 1 byte.
|
|
MinTxOutPayload = 11
|
|
|
|
// maxTxOutPerMessage is the maximum number of transactions outputs that
|
|
// a transaction which fits into a message could possibly have.
|
|
maxTxOutPerMessage = (MaxMessagePayload / MinTxOutPayload) + 1
|
|
|
|
// minTxPayload is the minimum payload size for a transaction. Note
|
|
// that any realistically usable transaction must have at least one
|
|
// input or output, but that is a rule enforced at a higher layer, so
|
|
// it is intentionally not included here.
|
|
// Version 4 bytes + Varint number of transaction inputs 1 byte + Varint
|
|
// number of transaction outputs 1 byte + LockTime 4 bytes + min input
|
|
// payload + min output payload.
|
|
minTxPayload = 10
|
|
)
|
|
|
|
// Outpoint defines a kaspa data type that is used to track previous
|
|
// transaction outputs.
|
|
type Outpoint struct {
|
|
TxID externalapi.DomainTransactionID
|
|
Index uint32
|
|
}
|
|
|
|
// NewOutpoint returns a new kaspa transaction outpoint point with the
|
|
// provided hash and index.
|
|
func NewOutpoint(txID *externalapi.DomainTransactionID, index uint32) *Outpoint {
|
|
return &Outpoint{
|
|
TxID: *txID,
|
|
Index: index,
|
|
}
|
|
}
|
|
|
|
// String returns the Outpoint in the human-readable form "txID:index".
|
|
func (o Outpoint) String() string {
|
|
// Allocate enough for ID string, colon, and 10 digits. Although
|
|
// at the time of writing, the number of digits can be no greater than
|
|
// the length of the decimal representation of maxTxOutPerMessage, the
|
|
// maximum message payload may increase in the future and this
|
|
// optimization may go unnoticed, so allocate space for 10 decimal
|
|
// digits, which will fit any uint32.
|
|
buf := make([]byte, 2*externalapi.DomainHashSize+1, 2*externalapi.DomainHashSize+1+10)
|
|
copy(buf, o.TxID.String())
|
|
buf[2*externalapi.DomainHashSize] = ':'
|
|
buf = strconv.AppendUint(buf, uint64(o.Index), 10)
|
|
return string(buf)
|
|
}
|
|
|
|
// TxIn defines a kaspa transaction input.
|
|
type TxIn struct {
|
|
PreviousOutpoint Outpoint
|
|
SignatureScript []byte
|
|
Sequence uint64
|
|
SigOpCount byte
|
|
}
|
|
|
|
// NewTxIn returns a new kaspa transaction input with the provided
|
|
// previous outpoint point and signature script with a default sequence of
|
|
// MaxTxInSequenceNum.
|
|
func NewTxIn(prevOut *Outpoint, signatureScript []byte, sequence uint64, sigOpCount byte) *TxIn {
|
|
return &TxIn{
|
|
PreviousOutpoint: *prevOut,
|
|
SignatureScript: signatureScript,
|
|
Sequence: sequence,
|
|
SigOpCount: sigOpCount,
|
|
}
|
|
}
|
|
|
|
// TxOut defines a kaspa transaction output.
|
|
type TxOut struct {
|
|
Value uint64
|
|
ScriptPubKey *externalapi.ScriptPublicKey
|
|
}
|
|
|
|
// NewTxOut returns a new kaspa transaction output with the provided
|
|
// transaction value and public key script.
|
|
func NewTxOut(value uint64, scriptPubKey *externalapi.ScriptPublicKey) *TxOut {
|
|
return &TxOut{
|
|
Value: value,
|
|
ScriptPubKey: scriptPubKey,
|
|
}
|
|
}
|
|
|
|
// MsgTx implements the Message interface and represents a kaspa tx message.
|
|
// It is used to deliver transaction information in response to a getdata
|
|
// message (MsgGetData) for a given transaction.
|
|
//
|
|
// Use the AddTxIn and AddTxOut functions to build up the list of transaction
|
|
// inputs and outputs.
|
|
type MsgTx struct {
|
|
baseMessage
|
|
Version uint16
|
|
TxIn []*TxIn
|
|
TxOut []*TxOut
|
|
LockTime uint64
|
|
SubnetworkID externalapi.DomainSubnetworkID
|
|
Gas uint64
|
|
Payload []byte
|
|
}
|
|
|
|
// AddTxIn adds a transaction input to the message.
|
|
func (msg *MsgTx) AddTxIn(ti *TxIn) {
|
|
msg.TxIn = append(msg.TxIn, ti)
|
|
}
|
|
|
|
// AddTxOut adds a transaction output to the message.
|
|
func (msg *MsgTx) AddTxOut(to *TxOut) {
|
|
msg.TxOut = append(msg.TxOut, to)
|
|
}
|
|
|
|
// IsCoinBase determines whether or not a transaction is a coinbase transaction. A coinbase
|
|
// transaction is a special transaction created by miners that distributes fees and block subsidy
|
|
// to the previous blocks' miners, and to specify the scriptPubKey that will be used to pay the current
|
|
// miner in future blocks. Each input of the coinbase transaction should set index to maximum
|
|
// value and reference the relevant block id, instead of previous transaction id.
|
|
func (msg *MsgTx) IsCoinBase() bool {
|
|
// A coinbase transaction must have subnetwork id SubnetworkIDCoinbase
|
|
return msg.SubnetworkID == subnetworks.SubnetworkIDCoinbase
|
|
}
|
|
|
|
// TxHash generates the Hash for the transaction.
|
|
func (msg *MsgTx) TxHash() *externalapi.DomainHash {
|
|
return consensushashing.TransactionHash(MsgTxToDomainTransaction(msg))
|
|
}
|
|
|
|
// TxID generates the Hash for the transaction without the signature script, gas and payload fields.
|
|
func (msg *MsgTx) TxID() *externalapi.DomainTransactionID {
|
|
return consensushashing.TransactionID(MsgTxToDomainTransaction(msg))
|
|
}
|
|
|
|
// Copy creates a deep copy of a transaction so that the original does not get
|
|
// modified when the copy is manipulated.
|
|
func (msg *MsgTx) Copy() *MsgTx {
|
|
// Create new tx and start by copying primitive values and making space
|
|
// for the transaction inputs and outputs.
|
|
newTx := MsgTx{
|
|
Version: msg.Version,
|
|
TxIn: make([]*TxIn, 0, len(msg.TxIn)),
|
|
TxOut: make([]*TxOut, 0, len(msg.TxOut)),
|
|
LockTime: msg.LockTime,
|
|
SubnetworkID: msg.SubnetworkID,
|
|
Gas: msg.Gas,
|
|
}
|
|
|
|
if msg.Payload != nil {
|
|
newTx.Payload = make([]byte, len(msg.Payload))
|
|
copy(newTx.Payload, msg.Payload)
|
|
}
|
|
|
|
// Deep copy the old TxIn data.
|
|
for _, oldTxIn := range msg.TxIn {
|
|
// Deep copy the old previous outpoint.
|
|
oldOutpoint := oldTxIn.PreviousOutpoint
|
|
newOutpoint := Outpoint{}
|
|
newOutpoint.TxID = oldOutpoint.TxID
|
|
newOutpoint.Index = oldOutpoint.Index
|
|
|
|
// Deep copy the old signature script.
|
|
var newScript []byte
|
|
oldScript := oldTxIn.SignatureScript
|
|
oldScriptLen := len(oldScript)
|
|
if oldScriptLen > 0 {
|
|
newScript = make([]byte, oldScriptLen)
|
|
copy(newScript, oldScript[:oldScriptLen])
|
|
}
|
|
|
|
// Create new txIn with the deep copied data.
|
|
newTxIn := TxIn{
|
|
PreviousOutpoint: newOutpoint,
|
|
SignatureScript: newScript,
|
|
Sequence: oldTxIn.Sequence,
|
|
SigOpCount: oldTxIn.SigOpCount,
|
|
}
|
|
|
|
// Finally, append this fully copied txin.
|
|
newTx.TxIn = append(newTx.TxIn, &newTxIn)
|
|
}
|
|
|
|
// Deep copy the old TxOut data.
|
|
for _, oldTxOut := range msg.TxOut {
|
|
// Deep copy the old ScriptPublicKey
|
|
var newScript externalapi.ScriptPublicKey
|
|
oldScript := oldTxOut.ScriptPubKey
|
|
oldScriptLen := len(oldScript.Script)
|
|
if oldScriptLen > 0 {
|
|
newScript = externalapi.ScriptPublicKey{Script: make([]byte, oldScriptLen), Version: oldScript.Version}
|
|
copy(newScript.Script, oldScript.Script[:oldScriptLen])
|
|
}
|
|
|
|
// Create new txOut with the deep copied data and append it to
|
|
// new Tx.
|
|
newTxOut := TxOut{
|
|
Value: oldTxOut.Value,
|
|
ScriptPubKey: &newScript,
|
|
}
|
|
newTx.TxOut = append(newTx.TxOut, &newTxOut)
|
|
}
|
|
|
|
return &newTx
|
|
}
|
|
|
|
// Command returns the protocol command string for the message. This is part
|
|
// of the Message interface implementation.
|
|
func (msg *MsgTx) Command() MessageCommand {
|
|
return CmdTx
|
|
}
|
|
|
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
|
// receiver. This is part of the Message interface implementation.
|
|
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
|
|
return MaxMessagePayload
|
|
}
|
|
|
|
// IsSubnetworkCompatible return true iff subnetworkID is one or more of the following:
|
|
// 1. The SupportsAll subnetwork (full node)
|
|
// 2. The native subnetwork
|
|
// 3. The transaction's subnetwork
|
|
func (msg *MsgTx) IsSubnetworkCompatible(subnetworkID *externalapi.DomainSubnetworkID) bool {
|
|
return subnetworkID == nil ||
|
|
subnetworkID.Equal(&subnetworks.SubnetworkIDNative) ||
|
|
subnetworkID.Equal(&msg.SubnetworkID)
|
|
}
|
|
|
|
// newMsgTx returns a new tx message that conforms to the Message interface.
|
|
//
|
|
// All fields except version and gas has default values if nil is passed:
|
|
// txIn, txOut - empty arrays
|
|
// payload - an empty payload
|
|
//
|
|
// The payload hash is calculated automatically according to provided payload.
|
|
// Also, the lock time is set to zero to indicate the transaction is valid
|
|
// immediately as opposed to some time in future.
|
|
func newMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
|
gas uint64, payload []byte, lockTime uint64) *MsgTx {
|
|
|
|
if txIn == nil {
|
|
txIn = make([]*TxIn, 0, defaultTxInOutAlloc)
|
|
}
|
|
|
|
if txOut == nil {
|
|
txOut = make([]*TxOut, 0, defaultTxInOutAlloc)
|
|
}
|
|
|
|
return &MsgTx{
|
|
Version: version,
|
|
TxIn: txIn,
|
|
TxOut: txOut,
|
|
SubnetworkID: *subnetworkID,
|
|
Gas: gas,
|
|
Payload: payload,
|
|
LockTime: lockTime,
|
|
}
|
|
}
|
|
|
|
// NewNativeMsgTx returns a new tx message in the native subnetwork
|
|
func NewNativeMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut) *MsgTx {
|
|
return newMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDNative, 0, nil, 0)
|
|
}
|
|
|
|
// NewSubnetworkMsgTx returns a new tx message in the specified subnetwork with specified gas and payload
|
|
func NewSubnetworkMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, subnetworkID *externalapi.DomainSubnetworkID,
|
|
gas uint64, payload []byte) *MsgTx {
|
|
|
|
return newMsgTx(version, txIn, txOut, subnetworkID, gas, payload, 0)
|
|
}
|
|
|
|
// NewNativeMsgTxWithLocktime returns a new tx message in the native subnetwork with a locktime.
|
|
//
|
|
// See newMsgTx for further documntation of the parameters
|
|
func NewNativeMsgTxWithLocktime(version uint16, txIn []*TxIn, txOut []*TxOut, locktime uint64) *MsgTx {
|
|
return newMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDNative, 0, nil, locktime)
|
|
}
|
|
|
|
// NewRegistryMsgTx creates a new MsgTx that registers a new subnetwork
|
|
func NewRegistryMsgTx(version uint16, txIn []*TxIn, txOut []*TxOut, gasLimit uint64) *MsgTx {
|
|
payload := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(payload, gasLimit)
|
|
|
|
return NewSubnetworkMsgTx(version, txIn, txOut, &subnetworks.SubnetworkIDRegistry, 0, payload)
|
|
}
|