rules.go raw

   1  // Package txrules provides transaction rules that should be followed by
   2  // transaction authors for wide mempool acceptance and quick mining.
   3  package txrules
   4  
   5  import (
   6  	"errors"
   7  	"github.com/p9c/p9/pkg/amt"
   8  	
   9  	"github.com/p9c/p9/pkg/txscript"
  10  	"github.com/p9c/p9/pkg/wire"
  11  )
  12  
  13  // DefaultRelayFeePerKb is the default minimum relay fee policy for a mempool.
  14  const DefaultRelayFeePerKb amt.Amount = 1e3
  15  
  16  // Transaction rule violations
  17  var (
  18  	ErrAmountNegative   = errors.New("transaction output amount is negative")
  19  	ErrAmountExceedsMax = errors.New("transaction output amount exceeds maximum value")
  20  	ErrOutputIsDust     = errors.New("transaction output is dust")
  21  )
  22  
  23  // GetDustThreshold is used to define the amount below which output will be determined as dust. Threshold is determined
  24  // as 3 times the relay fee.
  25  func GetDustThreshold(scriptSize int, relayFeePerKb amt.Amount) amt.Amount {
  26  	// Calculate the total (estimated) cost to the network. This is calculated using the serialize size of the output
  27  	// plus the serial size of a transaction input which redeems it. The output is assumed to be compressed P2PKH as
  28  	// this is the most common script type. Use the average size of a compressed P2PKH redeem input (148) rather than
  29  	// the largest possible (txsizes.RedeemP2PKHInputSize).
  30  	totalSize := 8 + wire.VarIntSerializeSize(uint64(scriptSize)) +
  31  		scriptSize + 148
  32  	byteFee := relayFeePerKb / 1000
  33  	relayFee := amt.Amount(totalSize) * byteFee
  34  	return 3 * relayFee
  35  }
  36  
  37  // IsDustAmount determines whether a transaction output value and script length would cause the output to be considered
  38  // dust. Transactions with dust outputs are not standard and are rejected by mempools with default policies.
  39  func IsDustAmount(amount amt.Amount, scriptSize int, relayFeePerKb amt.Amount) bool {
  40  	return amount < GetDustThreshold(scriptSize, relayFeePerKb)
  41  }
  42  
  43  // IsDustOutput determines whether a transaction output is considered dust. Transactions with dust outputs are not
  44  // standard and are rejected by mempools with default policies.
  45  func IsDustOutput(output *wire.TxOut, relayFeePerKb amt.Amount) bool {
  46  	// Unspendable outputs which solely carry data are not checked for dust.
  47  	if txscript.GetScriptClass(output.PkScript) == txscript.NullDataTy {
  48  		return false
  49  	}
  50  	// All other unspendable outputs are considered dust.
  51  	if txscript.IsUnspendable(output.PkScript) {
  52  		return true
  53  	}
  54  	return IsDustAmount(
  55  		amt.Amount(output.Value), len(output.PkScript),
  56  		relayFeePerKb,
  57  	)
  58  }
  59  
  60  // CheckOutput performs simple consensus and policy tests on a transaction output.
  61  func CheckOutput(output *wire.TxOut, relayFeePerKb amt.Amount) (e error) {
  62  	if output.Value < 0 {
  63  		return ErrAmountNegative
  64  	}
  65  	if output.Value > int64(amt.MaxSatoshi) {
  66  		return ErrAmountExceedsMax
  67  	}
  68  	if IsDustOutput(output, relayFeePerKb) {
  69  		return ErrOutputIsDust
  70  	}
  71  	return nil
  72  }
  73  
  74  // FeeForSerializeSize calculates the required fee for a transaction of some arbitrary size given a mempool's relay fee
  75  // policy.
  76  func FeeForSerializeSize(relayFeePerKb amt.Amount, txSerializeSize int) amt.Amount {
  77  	fee := relayFeePerKb * amt.Amount(txSerializeSize) / 1000
  78  	if fee == 0 && relayFeePerKb > 0 {
  79  		fee = relayFeePerKb
  80  	}
  81  	if fee < 0 || fee > amt.MaxSatoshi {
  82  		fee = amt.MaxSatoshi
  83  	}
  84  	return fee
  85  }
  86