mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-07 14:46:44 +00:00
[NOD-237] Implement transaction mass (#355)
* [NOD-237] Implemented transaction mass. * [NOD-237] Added transaction mass validation to the mempool. * [NOD-237] Made blockMaxMassMax not rely on MaxBlockPayload. * [NOD-237] Added comments describing the new constants in validate.go. * [NOD-237] Changed the default blockmaxmass to 10,000,000. * [NOD-237] Fixed a comment that erroneously didn't refer to mass. * [NOD-237] Added comments to ValidateTxMass and CalcTxMass. * [NOD-237] Renamed "size" to "byte". Made validateBlockMass exit early if validation fails. Fixed unit names in comments. In CalcTxMass, moved summing of mass to the bottom of the function. * [NOD-237] Instead of ErrMassTooHigh, renamed ErrBlockTooBig and ErrTxTooBig. Replaced wire.MaxBlockPayload with MaxMassPerBlock. * [NOD-237] Fixed sanity checks related to block size in commands. * [NOD-237] To use up less memory during testing, made the mass in the "too big" test come from pkScripts rather than input bytes. * [NOD-237] Added an overflow check to validateBlockMass.
This commit is contained in:
parent
54b681460d
commit
bfdf7a2cf2
@ -37,9 +37,9 @@ const (
|
|||||||
// exists.
|
// exists.
|
||||||
ErrDuplicateBlock ErrorCode = iota
|
ErrDuplicateBlock ErrorCode = iota
|
||||||
|
|
||||||
// ErrBlockTooBig indicates the serialized block size exceeds the
|
// ErrBlockMassTooHigh indicates the mass of a block exceeds the maximum
|
||||||
// maximum allowed size.
|
// allowed limits.
|
||||||
ErrBlockTooBig
|
ErrBlockMassTooHigh
|
||||||
|
|
||||||
// ErrBlockVersionTooOld indicates the block version is too old and is
|
// ErrBlockVersionTooOld indicates the block version is too old and is
|
||||||
// no longer accepted since the majority of the network has upgraded
|
// no longer accepted since the majority of the network has upgraded
|
||||||
@ -105,9 +105,9 @@ const (
|
|||||||
// valid transaction must have at least one input.
|
// valid transaction must have at least one input.
|
||||||
ErrNoTxInputs
|
ErrNoTxInputs
|
||||||
|
|
||||||
// ErrTxTooBig indicates a transaction exceeds the maximum allowed size
|
// ErrTxMassTooHigh indicates the mass of a transaction exceeds the maximum
|
||||||
// when serialized.
|
// allowed limits.
|
||||||
ErrTxTooBig
|
ErrTxMassTooHigh
|
||||||
|
|
||||||
// ErrBadTxOutValue indicates an output value for a transaction is
|
// ErrBadTxOutValue indicates an output value for a transaction is
|
||||||
// invalid in some way such as being out of range.
|
// invalid in some way such as being out of range.
|
||||||
@ -227,7 +227,7 @@ const (
|
|||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
var errorCodeStrings = map[ErrorCode]string{
|
var errorCodeStrings = map[ErrorCode]string{
|
||||||
ErrDuplicateBlock: "ErrDuplicateBlock",
|
ErrDuplicateBlock: "ErrDuplicateBlock",
|
||||||
ErrBlockTooBig: "ErrBlockTooBig",
|
ErrBlockMassTooHigh: "ErrBlockMassTooHigh",
|
||||||
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
|
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
|
||||||
ErrInvalidTime: "ErrInvalidTime",
|
ErrInvalidTime: "ErrInvalidTime",
|
||||||
ErrTimeTooOld: "ErrTimeTooOld",
|
ErrTimeTooOld: "ErrTimeTooOld",
|
||||||
@ -242,7 +242,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||||||
ErrFinalityPointTimeTooOld: "ErrFinalityPointTimeTooOld",
|
ErrFinalityPointTimeTooOld: "ErrFinalityPointTimeTooOld",
|
||||||
ErrNoTransactions: "ErrNoTransactions",
|
ErrNoTransactions: "ErrNoTransactions",
|
||||||
ErrNoTxInputs: "ErrNoTxInputs",
|
ErrNoTxInputs: "ErrNoTxInputs",
|
||||||
ErrTxTooBig: "ErrTxTooBig",
|
ErrTxMassTooHigh: "ErrTxMassTooHigh",
|
||||||
ErrBadTxOutValue: "ErrBadTxOutValue",
|
ErrBadTxOutValue: "ErrBadTxOutValue",
|
||||||
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
|
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
|
||||||
ErrBadTxInput: "ErrBadTxInput",
|
ErrBadTxInput: "ErrBadTxInput",
|
||||||
|
@ -16,7 +16,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{ErrDuplicateBlock, "ErrDuplicateBlock"},
|
{ErrDuplicateBlock, "ErrDuplicateBlock"},
|
||||||
{ErrBlockTooBig, "ErrBlockTooBig"},
|
{ErrBlockMassTooHigh, "ErrBlockMassTooHigh"},
|
||||||
{ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
|
{ErrBlockVersionTooOld, "ErrBlockVersionTooOld"},
|
||||||
{ErrInvalidTime, "ErrInvalidTime"},
|
{ErrInvalidTime, "ErrInvalidTime"},
|
||||||
{ErrTimeTooOld, "ErrTimeTooOld"},
|
{ErrTimeTooOld, "ErrTimeTooOld"},
|
||||||
@ -31,7 +31,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||||||
{ErrFinalityPointTimeTooOld, "ErrFinalityPointTimeTooOld"},
|
{ErrFinalityPointTimeTooOld, "ErrFinalityPointTimeTooOld"},
|
||||||
{ErrNoTransactions, "ErrNoTransactions"},
|
{ErrNoTransactions, "ErrNoTransactions"},
|
||||||
{ErrNoTxInputs, "ErrNoTxInputs"},
|
{ErrNoTxInputs, "ErrNoTxInputs"},
|
||||||
{ErrTxTooBig, "ErrTxTooBig"},
|
{ErrTxMassTooHigh, "ErrTxMassTooHigh"},
|
||||||
{ErrBadTxOutValue, "ErrBadTxOutValue"},
|
{ErrBadTxOutValue, "ErrBadTxOutValue"},
|
||||||
{ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
|
{ErrDuplicateTxInputs, "ErrDuplicateTxInputs"},
|
||||||
{ErrBadTxInput, "ErrBadTxInput"},
|
{ErrBadTxInput, "ErrBadTxInput"},
|
||||||
|
@ -1106,7 +1106,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) {
|
|||||||
replaceSpendScript(sizePadScript)(b)
|
replaceSpendScript(sizePadScript)(b)
|
||||||
})
|
})
|
||||||
g.assertTipBlockSize(maxBlockSize + 1)
|
g.assertTipBlockSize(maxBlockSize + 1)
|
||||||
rejected(blockdag.ErrBlockTooBig)
|
rejected(blockdag.ErrBlockMassTooHigh)
|
||||||
|
|
||||||
// Parent was rejected, so this block must either be an orphan or
|
// Parent was rejected, so this block must either be an orphan or
|
||||||
// outright rejected due to an invalid parent.
|
// outright rejected due to an invalid parent.
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
// MaxSigOpsPerBlock is the maximum number of signature operations
|
// MaxSigOpsPerBlock is the maximum number of signature operations
|
||||||
// allowed for a block. It is a fraction of the max block payload size.
|
// allowed for a block. It is a fraction of the max block transaction mass.
|
||||||
MaxSigOpsPerBlock = wire.MaxBlockPayload / 50
|
MaxSigOpsPerBlock = wire.MaxMassPerBlock / 50
|
||||||
|
|
||||||
// MaxCoinbasePayloadLen is the maximum length a coinbase payload can be.
|
// MaxCoinbasePayloadLen is the maximum length a coinbase payload can be.
|
||||||
MaxCoinbasePayloadLen = 150
|
MaxCoinbasePayloadLen = 150
|
||||||
@ -29,6 +29,11 @@ const (
|
|||||||
// baseSubsidy is the starting subsidy amount for mined blocks. This
|
// baseSubsidy is the starting subsidy amount for mined blocks. This
|
||||||
// value is halved every SubsidyHalvingInterval blocks.
|
// value is halved every SubsidyHalvingInterval blocks.
|
||||||
baseSubsidy = 50 * util.SatoshiPerBitcoin
|
baseSubsidy = 50 * util.SatoshiPerBitcoin
|
||||||
|
|
||||||
|
// the following are used when calculating a transaction's mass
|
||||||
|
massPerTxByte = 1
|
||||||
|
massPerPKScriptByte = 10
|
||||||
|
massPerSigOp = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
// isNullOutpoint determines whether or not a previous transaction outpoint
|
// isNullOutpoint determines whether or not a previous transaction outpoint
|
||||||
@ -121,13 +126,13 @@ func CheckTransactionSanity(tx *util.Tx, subnetworkID *subnetworkid.SubnetworkID
|
|||||||
return ruleError(ErrNoTxInputs, "transaction has no inputs")
|
return ruleError(ErrNoTxInputs, "transaction has no inputs")
|
||||||
}
|
}
|
||||||
|
|
||||||
// A transaction must not exceed the maximum allowed block payload when
|
// A transaction must not exceed the maximum allowed block mass when
|
||||||
// serialized.
|
// serialized.
|
||||||
serializedTxSize := msgTx.SerializeSize()
|
serializedTxSize := msgTx.SerializeSize()
|
||||||
if serializedTxSize > wire.MaxBlockPayload {
|
if serializedTxSize > wire.MaxMassPerBlock {
|
||||||
str := fmt.Sprintf("serialized transaction is too big - got "+
|
str := fmt.Sprintf("serialized transaction is too big - got "+
|
||||||
"%d, max %d", serializedTxSize, wire.MaxBlockPayload)
|
"%d, max %d", serializedTxSize, wire.MaxMassPerBlock)
|
||||||
return ruleError(ErrTxTooBig, str)
|
return ruleError(ErrTxMassTooHigh, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the transaction amounts are in range. Each transaction
|
// Ensure the transaction amounts are in range. Each transaction
|
||||||
@ -361,6 +366,86 @@ func CountP2SHSigOps(tx *util.Tx, isCoinbase bool, utxoSet UTXOSet) (int, error)
|
|||||||
return totalSigOps, nil
|
return totalSigOps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateTxMass makes sure that the given transaction's mass does not exceed
|
||||||
|
// the maximum allowed limit. Currently, it is equivalent to the block mass limit.
|
||||||
|
// See CalcTxMass for further details.
|
||||||
|
func ValidateTxMass(tx *util.Tx, utxoSet UTXOSet) error {
|
||||||
|
txMass, err := CalcTxMass(tx, utxoSet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if txMass > wire.MaxMassPerBlock {
|
||||||
|
str := fmt.Sprintf("tx %s has mass %d, which is above the "+
|
||||||
|
"allowed limit of %d", tx.ID(), txMass, wire.MaxMassPerBlock)
|
||||||
|
return ruleError(ErrTxMassTooHigh, str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateBlockMass(pastUTXO UTXOSet, transactions []*util.Tx) error {
|
||||||
|
totalMass := uint64(0)
|
||||||
|
for _, tx := range transactions {
|
||||||
|
txMass, err := CalcTxMass(tx, pastUTXO)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
totalMass += txMass
|
||||||
|
|
||||||
|
// We could potentially overflow the accumulator so check for
|
||||||
|
// overflow as well.
|
||||||
|
if totalMass < txMass || totalMass > wire.MaxMassPerBlock {
|
||||||
|
str := fmt.Sprintf("block has total mass %d, which is "+
|
||||||
|
"above the allowed limit of %d", totalMass, wire.MaxMassPerBlock)
|
||||||
|
return ruleError(ErrBlockMassTooHigh, str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalcTxMass sums up and returns the "mass" of a transaction. This number
|
||||||
|
// is an approximation of how many resources (CPU, RAM, etc.) it would take
|
||||||
|
// to process the transaction.
|
||||||
|
// The following properties are considered in the calculation:
|
||||||
|
// * The transaction length in bytes
|
||||||
|
// * The length of all output scripts in bytes
|
||||||
|
// * The count of all input sigOps
|
||||||
|
func CalcTxMass(tx *util.Tx, utxoSet UTXOSet) (uint64, error) {
|
||||||
|
txSize := tx.MsgTx().SerializeSize()
|
||||||
|
|
||||||
|
if tx.IsCoinBase() {
|
||||||
|
return uint64(txSize * massPerTxByte), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkScriptSize := 0
|
||||||
|
for _, txOut := range tx.MsgTx().TxOut {
|
||||||
|
pkScriptSize += len(txOut.PkScript)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigOpsCount := 0
|
||||||
|
for txInIndex, txIn := range tx.MsgTx().TxIn {
|
||||||
|
// Ensure the referenced input transaction is available.
|
||||||
|
entry, ok := utxoSet.Get(txIn.PreviousOutpoint)
|
||||||
|
if !ok {
|
||||||
|
str := fmt.Sprintf("output %s referenced from "+
|
||||||
|
"transaction %s:%d either does not exist or "+
|
||||||
|
"has already been spent", txIn.PreviousOutpoint,
|
||||||
|
tx.ID(), txInIndex)
|
||||||
|
return 0, ruleError(ErrMissingTxOut, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count the precise number of signature operations in the
|
||||||
|
// referenced public key script.
|
||||||
|
pkScript := entry.PkScript()
|
||||||
|
sigScript := txIn.SignatureScript
|
||||||
|
isP2SH := txscript.IsPayToScriptHash(pkScript)
|
||||||
|
sigOpsCount += txscript.GetPreciseSigOpCount(sigScript, pkScript, isP2SH)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint64(txSize*massPerTxByte +
|
||||||
|
pkScriptSize*massPerPKScriptByte +
|
||||||
|
sigOpsCount*massPerSigOp), nil
|
||||||
|
}
|
||||||
|
|
||||||
// checkBlockHeaderSanity performs some preliminary checks on a block header to
|
// checkBlockHeaderSanity performs some preliminary checks on a block header to
|
||||||
// ensure it is sane before continuing with processing. These checks are
|
// ensure it is sane before continuing with processing. These checks are
|
||||||
// context free.
|
// context free.
|
||||||
@ -444,21 +529,12 @@ func (dag *BlockDAG) checkBlockSanity(block *util.Block, flags BehaviorFlags) (t
|
|||||||
"any transactions")
|
"any transactions")
|
||||||
}
|
}
|
||||||
|
|
||||||
// A block must not have more transactions than the max block payload or
|
// A block must not have more transactions than the max block mass or
|
||||||
// else it is certainly over the block size limit.
|
// else it is certainly over the block mass limit.
|
||||||
if numTx > wire.MaxBlockPayload {
|
if numTx > wire.MaxMassPerBlock {
|
||||||
str := fmt.Sprintf("block contains too many transactions - "+
|
str := fmt.Sprintf("block contains too many transactions - "+
|
||||||
"got %d, max %d", numTx, wire.MaxBlockPayload)
|
"got %d, max %d", numTx, wire.MaxMassPerBlock)
|
||||||
return 0, ruleError(ErrBlockTooBig, str)
|
return 0, ruleError(ErrBlockMassTooHigh, str)
|
||||||
}
|
|
||||||
|
|
||||||
// A block must not exceed the maximum allowed block payload when
|
|
||||||
// serialized.
|
|
||||||
serializedSize := msgBlock.SerializeSize()
|
|
||||||
if serializedSize > wire.MaxBlockPayload {
|
|
||||||
str := fmt.Sprintf("serialized block is too big - got %d, "+
|
|
||||||
"max %d", serializedSize, wire.MaxBlockPayload)
|
|
||||||
return 0, ruleError(ErrBlockTooBig, str)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first transaction in a block must be a coinbase.
|
// The first transaction in a block must be a coinbase.
|
||||||
@ -861,6 +937,10 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
|
|||||||
if err := validateSigopsCount(pastUTXO, transactions); err != nil {
|
if err := validateSigopsCount(pastUTXO, transactions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateBlockMass(pastUTXO, transactions); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform several checks on the inputs for each transaction. Also
|
// Perform several checks on the inputs for each transaction. Also
|
||||||
|
@ -633,7 +633,7 @@ func TestCheckTransactionSanity(t *testing.T) {
|
|||||||
{"good one", 1, 1, 1, *subnetworkid.SubnetworkIDNative, nil, nil, nil},
|
{"good one", 1, 1, 1, *subnetworkid.SubnetworkIDNative, nil, nil, nil},
|
||||||
{"no inputs", 0, 1, 1, *subnetworkid.SubnetworkIDNative, nil, nil, ruleError(ErrNoTxInputs, "")},
|
{"no inputs", 0, 1, 1, *subnetworkid.SubnetworkIDNative, nil, nil, ruleError(ErrNoTxInputs, "")},
|
||||||
{"no outputs", 1, 0, 1, *subnetworkid.SubnetworkIDNative, nil, nil, nil},
|
{"no outputs", 1, 0, 1, *subnetworkid.SubnetworkIDNative, nil, nil, nil},
|
||||||
{"too big", 100000, 1, 1, *subnetworkid.SubnetworkIDNative, nil, nil, ruleError(ErrTxTooBig, "")},
|
{"too massive", 1, 1000000, 1, *subnetworkid.SubnetworkIDNative, nil, nil, ruleError(ErrTxMassTooHigh, "")},
|
||||||
{"too much satoshi in one output", 1, 1, util.MaxSatoshi + 1,
|
{"too much satoshi in one output", 1, 1, util.MaxSatoshi + 1,
|
||||||
*subnetworkid.SubnetworkIDNative,
|
*subnetworkid.SubnetworkIDNative,
|
||||||
nil,
|
nil,
|
||||||
|
@ -222,10 +222,10 @@ type TemplateRequest struct {
|
|||||||
// Optional long polling.
|
// Optional long polling.
|
||||||
LongPollID string `json:"longPollId,omitempty"`
|
LongPollID string `json:"longPollId,omitempty"`
|
||||||
|
|
||||||
// Optional template tweaking. SigOpLimit and SizeLimit can be int64
|
// Optional template tweaking. SigOpLimit and MassLimit can be int64
|
||||||
// or bool.
|
// or bool.
|
||||||
SigOpLimit interface{} `json:"sigOpLimit,omitempty"`
|
SigOpLimit interface{} `json:"sigOpLimit,omitempty"`
|
||||||
SizeLimit interface{} `json:"sizeLimit,omitempty"`
|
MassLimit interface{} `json:"massLimit,omitempty"`
|
||||||
MaxVersion uint32 `json:"maxVersion,omitempty"`
|
MaxVersion uint32 `json:"maxVersion,omitempty"`
|
||||||
|
|
||||||
// Basic pool extension from BIP 0023.
|
// Basic pool extension from BIP 0023.
|
||||||
@ -257,7 +257,7 @@ func convertTemplateRequestField(fieldName string, iface interface{}) (interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON provides a custom Unmarshal method for TemplateRequest. This
|
// UnmarshalJSON provides a custom Unmarshal method for TemplateRequest. This
|
||||||
// is necessary because the SigOpLimit and SizeLimit fields can only be specific
|
// is necessary because the SigOpLimit and MassLimit fields can only be specific
|
||||||
// types.
|
// types.
|
||||||
func (t *TemplateRequest) UnmarshalJSON(data []byte) error {
|
func (t *TemplateRequest) UnmarshalJSON(data []byte) error {
|
||||||
type templateRequest TemplateRequest
|
type templateRequest TemplateRequest
|
||||||
@ -274,12 +274,12 @@ func (t *TemplateRequest) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
request.SigOpLimit = val
|
request.SigOpLimit = val
|
||||||
|
|
||||||
// The SizeLimit field can only be nil, bool, or int64.
|
// The MassLimit field can only be nil, bool, or int64.
|
||||||
val, err = convertTemplateRequestField("sizeLimit", request.SizeLimit)
|
val, err = convertTemplateRequestField("massLimit", request.MassLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
request.SizeLimit = val
|
request.MassLimit = val
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -271,25 +271,25 @@ func TestDAGSvrCmds(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "getBlockTemplate optional - template request with tweaks",
|
name: "getBlockTemplate optional - template request with tweaks",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
return btcjson.NewCmd("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"sizeLimit":100000000,"maxVersion":1}`)
|
return btcjson.NewCmd("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"massLimit":100000000,"maxVersion":1}`)
|
||||||
},
|
},
|
||||||
staticCmd: func() interface{} {
|
staticCmd: func() interface{} {
|
||||||
template := btcjson.TemplateRequest{
|
template := btcjson.TemplateRequest{
|
||||||
Mode: "template",
|
Mode: "template",
|
||||||
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
||||||
SigOpLimit: 500,
|
SigOpLimit: 500,
|
||||||
SizeLimit: 100000000,
|
MassLimit: 100000000,
|
||||||
MaxVersion: 1,
|
MaxVersion: 1,
|
||||||
}
|
}
|
||||||
return btcjson.NewGetBlockTemplateCmd(&template)
|
return btcjson.NewGetBlockTemplateCmd(&template)
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"sizeLimit":100000000,"maxVersion":1}],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":500,"massLimit":100000000,"maxVersion":1}],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetBlockTemplateCmd{
|
unmarshalled: &btcjson.GetBlockTemplateCmd{
|
||||||
Request: &btcjson.TemplateRequest{
|
Request: &btcjson.TemplateRequest{
|
||||||
Mode: "template",
|
Mode: "template",
|
||||||
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
||||||
SigOpLimit: int64(500),
|
SigOpLimit: int64(500),
|
||||||
SizeLimit: int64(100000000),
|
MassLimit: int64(100000000),
|
||||||
MaxVersion: 1,
|
MaxVersion: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -297,25 +297,25 @@ func TestDAGSvrCmds(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "getBlockTemplate optional - template request with tweaks 2",
|
name: "getBlockTemplate optional - template request with tweaks 2",
|
||||||
newCmd: func() (interface{}, error) {
|
newCmd: func() (interface{}, error) {
|
||||||
return btcjson.NewCmd("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"sizeLimit":100000000,"maxVersion":1}`)
|
return btcjson.NewCmd("getBlockTemplate", `{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"massLimit":100000000,"maxVersion":1}`)
|
||||||
},
|
},
|
||||||
staticCmd: func() interface{} {
|
staticCmd: func() interface{} {
|
||||||
template := btcjson.TemplateRequest{
|
template := btcjson.TemplateRequest{
|
||||||
Mode: "template",
|
Mode: "template",
|
||||||
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
||||||
SigOpLimit: true,
|
SigOpLimit: true,
|
||||||
SizeLimit: 100000000,
|
MassLimit: 100000000,
|
||||||
MaxVersion: 1,
|
MaxVersion: 1,
|
||||||
}
|
}
|
||||||
return btcjson.NewGetBlockTemplateCmd(&template)
|
return btcjson.NewGetBlockTemplateCmd(&template)
|
||||||
},
|
},
|
||||||
marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"sizeLimit":100000000,"maxVersion":1}],"id":1}`,
|
marshalled: `{"jsonrpc":"1.0","method":"getBlockTemplate","params":[{"mode":"template","capabilities":["longPoll","coinbaseTxn"],"sigOpLimit":true,"massLimit":100000000,"maxVersion":1}],"id":1}`,
|
||||||
unmarshalled: &btcjson.GetBlockTemplateCmd{
|
unmarshalled: &btcjson.GetBlockTemplateCmd{
|
||||||
Request: &btcjson.TemplateRequest{
|
Request: &btcjson.TemplateRequest{
|
||||||
Mode: "template",
|
Mode: "template",
|
||||||
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
Capabilities: []string{"longPoll", "coinbaseTxn"},
|
||||||
SigOpLimit: true,
|
SigOpLimit: true,
|
||||||
SizeLimit: int64(100000000),
|
MassLimit: int64(100000000),
|
||||||
MaxVersion: 1,
|
MaxVersion: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1152,9 +1152,9 @@ func TestDAGSvrCmdErrors(t *testing.T) {
|
|||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid template request sizelimit field",
|
name: "invalid template request masslimit field",
|
||||||
result: &btcjson.TemplateRequest{},
|
result: &btcjson.TemplateRequest{},
|
||||||
marshalled: `{"sizelimit":"invalid"}`,
|
marshalled: `{"masslimit":"invalid"}`,
|
||||||
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
err: btcjson.Error{ErrorCode: btcjson.ErrInvalidType},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,7 @@ type GetBlockTemplateResultTx struct {
|
|||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Depends []int64 `json:"depends"`
|
Depends []int64 `json:"depends"`
|
||||||
|
Mass uint64 `json:"mass"`
|
||||||
Fee uint64 `json:"fee"`
|
Fee uint64 `json:"fee"`
|
||||||
SigOps int64 `json:"sigOps"`
|
SigOps int64 `json:"sigOps"`
|
||||||
}
|
}
|
||||||
@ -141,7 +142,7 @@ type GetBlockTemplateResult struct {
|
|||||||
Height uint64 `json:"height"`
|
Height uint64 `json:"height"`
|
||||||
ParentHashes []string `json:"parentHashes"`
|
ParentHashes []string `json:"parentHashes"`
|
||||||
SigOpLimit int64 `json:"sigOpLimit,omitempty"`
|
SigOpLimit int64 `json:"sigOpLimit,omitempty"`
|
||||||
SizeLimit int64 `json:"sizeLimit,omitempty"`
|
MassLimit int64 `json:"massLimit,omitempty"`
|
||||||
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
||||||
AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"`
|
AcceptedIDMerkleRoot string `json:"acceptedIdMerkleRoot"`
|
||||||
UTXOCommitment string `json:"utxoCommitment"`
|
UTXOCommitment string `json:"utxoCommitment"`
|
||||||
|
@ -69,10 +69,10 @@ func (bi *blockImporter) readBlock() ([]byte, error) {
|
|||||||
if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil {
|
if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if blockLen > wire.MaxBlockPayload {
|
if blockLen > wire.MaxMessagePayload {
|
||||||
return nil, fmt.Errorf("block payload of %d bytes is larger "+
|
return nil, fmt.Errorf("block payload of %d bytes is larger "+
|
||||||
"than the max allowed %d bytes", blockLen,
|
"than the max allowed %d bytes", blockLen,
|
||||||
wire.MaxBlockPayload)
|
wire.MaxMessagePayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
serializedBlock := make([]byte, blockLen)
|
serializedBlock := make([]byte, blockLen)
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/daglabs/btcd/util/network"
|
"github.com/daglabs/btcd/util/network"
|
||||||
"github.com/daglabs/btcd/util/subnetworkid"
|
"github.com/daglabs/btcd/util/subnetworkid"
|
||||||
"github.com/daglabs/btcd/version"
|
"github.com/daglabs/btcd/version"
|
||||||
"github.com/daglabs/btcd/wire"
|
|
||||||
"github.com/jessevdk/go-flags"
|
"github.com/jessevdk/go-flags"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -47,10 +46,9 @@ const (
|
|||||||
defaultMaxRPCWebsockets = 25
|
defaultMaxRPCWebsockets = 25
|
||||||
defaultMaxRPCConcurrentReqs = 20
|
defaultMaxRPCConcurrentReqs = 20
|
||||||
defaultDbType = "ffldb"
|
defaultDbType = "ffldb"
|
||||||
defaultBlockMinSize = 0
|
defaultBlockMaxMass = 10000000
|
||||||
defaultBlockMaxSize = 750000
|
blockMaxMassMin = 1000
|
||||||
blockMaxSizeMin = 1000
|
blockMaxMassMax = 10000000
|
||||||
blockMaxSizeMax = wire.MaxBlockPayload - 1000
|
|
||||||
defaultGenerate = false
|
defaultGenerate = false
|
||||||
defaultMaxOrphanTransactions = 100
|
defaultMaxOrphanTransactions = 100
|
||||||
//DefaultMaxOrphanTxSize is the default maximum size for an orphan transaction
|
//DefaultMaxOrphanTxSize is the default maximum size for an orphan transaction
|
||||||
@ -148,8 +146,7 @@ type configFlags struct {
|
|||||||
MaxOrphanTxs int `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"`
|
MaxOrphanTxs int `long:"maxorphantx" description:"Max number of orphan transactions to keep in memory"`
|
||||||
Generate bool `long:"generate" description:"Generate (mine) bitcoins using the CPU"`
|
Generate bool `long:"generate" description:"Generate (mine) bitcoins using the CPU"`
|
||||||
MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"`
|
MiningAddrs []string `long:"miningaddr" description:"Add the specified payment address to the list of addresses to use for generated blocks -- At least one address is required if the generate option is set"`
|
||||||
BlockMinSize uint32 `long:"blockminsize" description:"Mininum block size in bytes to be used when creating a block"`
|
BlockMaxMass uint64 `long:"blockmaxmass" description:"Maximum transaction mass to be used when creating a block"`
|
||||||
BlockMaxSize uint32 `long:"blockmaxsize" description:"Maximum block size in bytes to be used when creating a block"`
|
|
||||||
UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."`
|
UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."`
|
||||||
NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"`
|
NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"`
|
||||||
EnableCFilters bool `long:"enablecfilters" description:"Enable committed filtering (CF) support"`
|
EnableCFilters bool `long:"enablecfilters" description:"Enable committed filtering (CF) support"`
|
||||||
@ -312,8 +309,7 @@ func loadConfig() (*Config, []string, error) {
|
|||||||
DbType: defaultDbType,
|
DbType: defaultDbType,
|
||||||
RPCKey: defaultRPCKeyFile,
|
RPCKey: defaultRPCKeyFile,
|
||||||
RPCCert: defaultRPCCertFile,
|
RPCCert: defaultRPCCertFile,
|
||||||
BlockMinSize: defaultBlockMinSize,
|
BlockMaxMass: defaultBlockMaxMass,
|
||||||
BlockMaxSize: defaultBlockMaxSize,
|
|
||||||
MaxOrphanTxs: defaultMaxOrphanTransactions,
|
MaxOrphanTxs: defaultMaxOrphanTransactions,
|
||||||
SigCacheMaxSize: defaultSigCacheMaxSize,
|
SigCacheMaxSize: defaultSigCacheMaxSize,
|
||||||
Generate: defaultGenerate,
|
Generate: defaultGenerate,
|
||||||
@ -659,14 +655,14 @@ func loadConfig() (*Config, []string, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit the max block size to a sane value.
|
// Limit the max block mass to a sane value.
|
||||||
if cfg.BlockMaxSize < blockMaxSizeMin || cfg.BlockMaxSize >
|
if cfg.BlockMaxMass < blockMaxMassMin || cfg.BlockMaxMass >
|
||||||
blockMaxSizeMax {
|
blockMaxMassMax {
|
||||||
|
|
||||||
str := "%s: The blockmaxsize option must be in between %d " +
|
str := "%s: The blockmaxmass option must be in between %d " +
|
||||||
"and %d -- parsed [%d]"
|
"and %d -- parsed [%d]"
|
||||||
err := fmt.Errorf(str, funcName, blockMaxSizeMin,
|
err := fmt.Errorf(str, funcName, blockMaxMassMin,
|
||||||
blockMaxSizeMax, cfg.BlockMaxSize)
|
blockMaxMassMax, cfg.BlockMaxMass)
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
fmt.Fprintln(os.Stderr, usageMessage)
|
fmt.Fprintln(os.Stderr, usageMessage)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -682,9 +678,6 @@ func loadConfig() (*Config, []string, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit minimum block sizes to max block size.
|
|
||||||
cfg.BlockMinSize = minUint32(cfg.BlockMinSize, cfg.BlockMaxSize)
|
|
||||||
|
|
||||||
// Look for illegal characters in the user agent comments.
|
// Look for illegal characters in the user agent comments.
|
||||||
for _, uaComment := range cfg.UserAgentComments {
|
for _, uaComment := range cfg.UserAgentComments {
|
||||||
if strings.ContainsAny(uaComment, "/:()") {
|
if strings.ContainsAny(uaComment, "/:()") {
|
||||||
|
@ -81,10 +81,10 @@ func (bi *blockImporter) readBlock() ([]byte, error) {
|
|||||||
if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil {
|
if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if blockLen > wire.MaxBlockPayload {
|
if blockLen > wire.MaxMessagePayload {
|
||||||
return nil, fmt.Errorf("block payload of %d bytes is larger "+
|
return nil, fmt.Errorf("block payload of %d bytes is larger "+
|
||||||
"than the max allowed %d bytes", blockLen,
|
"than the max allowed %d bytes", blockLen,
|
||||||
wire.MaxBlockPayload)
|
wire.MaxMessagePayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
serializedBlock := make([]byte, blockLen)
|
serializedBlock := make([]byte, blockLen)
|
||||||
|
@ -938,6 +938,16 @@ func (mp *TxPool) maybeAcceptTransaction(tx *util.Tx, isNew, rejectDupOrphans bo
|
|||||||
"transaction's sequence locks on inputs not met")
|
"transaction's sequence locks on inputs not met")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow transactions that exceed the maximum allowed
|
||||||
|
// transaction mass.
|
||||||
|
err = blockdag.ValidateTxMass(tx, mp.mpUTXOSet)
|
||||||
|
if err != nil {
|
||||||
|
if ruleError, ok := err.(blockdag.RuleError); ok {
|
||||||
|
return nil, nil, dagRuleError(ruleError)
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Perform several checks on the transaction inputs using the invariant
|
// Perform several checks on the transaction inputs using the invariant
|
||||||
// rules in blockchain for what transactions are allowed into blocks.
|
// rules in blockchain for what transactions are allowed into blocks.
|
||||||
// Also returns the fees associated with the transaction which will be
|
// Also returns the fees associated with the transaction which will be
|
||||||
|
@ -162,6 +162,10 @@ type BlockTemplate struct {
|
|||||||
// requirement.
|
// requirement.
|
||||||
Block *wire.MsgBlock
|
Block *wire.MsgBlock
|
||||||
|
|
||||||
|
// TxMasses contains the mass of each transaction in the generated
|
||||||
|
// template performs.
|
||||||
|
TxMasses []uint64
|
||||||
|
|
||||||
// Fees contains the amount of fees each transaction in the generated
|
// Fees contains the amount of fees each transaction in the generated
|
||||||
// template pays in base units. Since the first transaction is the
|
// template pays in base units. Since the first transaction is the
|
||||||
// coinbase, the first entry (offset 0) will contain the negative of the
|
// coinbase, the first entry (offset 0) will contain the negative of the
|
||||||
@ -279,7 +283,7 @@ func NewBlkTmplGenerator(policy *Policy, params *dagconfig.Params,
|
|||||||
// nonzero, in which case the block will be filled with the low-fee/free
|
// nonzero, in which case the block will be filled with the low-fee/free
|
||||||
// transactions until the block size reaches that minimum size.
|
// transactions until the block size reaches that minimum size.
|
||||||
//
|
//
|
||||||
// Any transactions which would cause the block to exceed the BlockMaxSize
|
// Any transactions which would cause the block to exceed the BlockMaxMass
|
||||||
// policy setting, exceed the maximum allowed signature operations per block, or
|
// policy setting, exceed the maximum allowed signature operations per block, or
|
||||||
// otherwise cause the block to be invalid are skipped.
|
// otherwise cause the block to be invalid are skipped.
|
||||||
//
|
//
|
||||||
@ -294,7 +298,7 @@ func NewBlkTmplGenerator(policy *Policy, params *dagconfig.Params,
|
|||||||
// |-----------------------------------| | --
|
// |-----------------------------------| | --
|
||||||
// | | |
|
// | | |
|
||||||
// | | |
|
// | | |
|
||||||
// | | |--- policy.BlockMaxSize
|
// | | |--- policy.BlockMaxMass
|
||||||
// | Transactions prioritized by fee | |
|
// | Transactions prioritized by fee | |
|
||||||
// | until <= policy.TxMinFreeFee | |
|
// | until <= policy.TxMinFreeFee | |
|
||||||
// | | |
|
// | | |
|
||||||
@ -310,6 +314,8 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
defer g.dag.RUnlock()
|
defer g.dag.RUnlock()
|
||||||
|
|
||||||
nextBlockBlueScore := g.dag.VirtualBlueScore()
|
nextBlockBlueScore := g.dag.VirtualBlueScore()
|
||||||
|
nextBlockUTXO := g.dag.UTXOSet()
|
||||||
|
|
||||||
coinbasePayloadPkScript, err := txscript.PayToAddrScript(payToAddress)
|
coinbasePayloadPkScript, err := txscript.PayToAddrScript(payToAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -328,6 +334,10 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
coinbaseTxMass, err := blockdag.CalcTxMass(coinbaseTx, nextBlockUTXO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
numCoinbaseSigOps := int64(blockdag.CountSigOps(coinbaseTx))
|
numCoinbaseSigOps := int64(blockdag.CountSigOps(coinbaseTx))
|
||||||
|
|
||||||
// Get the current source transactions and create a priority queue to
|
// Get the current source transactions and create a priority queue to
|
||||||
@ -346,21 +356,20 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
blockTxns := make([]*util.Tx, 0, len(sourceTxns)+1)
|
blockTxns := make([]*util.Tx, 0, len(sourceTxns)+1)
|
||||||
blockTxns = append(blockTxns, coinbaseTx)
|
blockTxns = append(blockTxns, coinbaseTx)
|
||||||
|
|
||||||
// The starting block size is the size of the block header plus the max
|
blockMass := coinbaseTxMass
|
||||||
// possible transaction count size, plus the size of the coinbase
|
|
||||||
// transaction.
|
|
||||||
blockSize := blockHeaderOverhead + uint32(coinbaseTx.MsgTx().SerializeSize())
|
|
||||||
blockSigOps := numCoinbaseSigOps
|
blockSigOps := numCoinbaseSigOps
|
||||||
totalFees := uint64(0)
|
totalFees := uint64(0)
|
||||||
|
|
||||||
// Create slices to hold the fees and number of signature operations
|
// Create slices to hold the mass, the fees, and number of signature
|
||||||
// for each of the selected transactions and add an entry for the
|
// operations for each of the selected transactions and add an entry for
|
||||||
// coinbase. This allows the code below to simply append details about
|
// the coinbase. This allows the code below to simply append details
|
||||||
// a transaction as it is selected for inclusion in the final block.
|
// about a transaction as it is selected for inclusion in the final block.
|
||||||
// However, since the total fees aren't known yet, use a dummy value for
|
// However, since the total fees aren't known yet, use a dummy value for
|
||||||
// the coinbase fee which will be updated later.
|
// the coinbase fee which will be updated later.
|
||||||
|
txMasses := make([]uint64, 0, len(sourceTxns)+1)
|
||||||
txFees := make([]uint64, 0, len(sourceTxns)+1)
|
txFees := make([]uint64, 0, len(sourceTxns)+1)
|
||||||
txSigOpCounts := make([]int64, 0, len(sourceTxns)+1)
|
txSigOpCounts := make([]int64, 0, len(sourceTxns)+1)
|
||||||
|
txMasses = append(txMasses, coinbaseTxMass)
|
||||||
txFees = append(txFees, 0) // For coinbase tx
|
txFees = append(txFees, 0) // For coinbase tx
|
||||||
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps)
|
txSigOpCounts = append(txSigOpCounts, numCoinbaseSigOps)
|
||||||
|
|
||||||
@ -423,14 +432,18 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
gasUsageMap[subnetworkID] = gasUsage + txGas
|
gasUsageMap[subnetworkID] = gasUsage + txGas
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce maximum block size. Also check for overflow.
|
// Enforce maximum transaction mass per block. Also check
|
||||||
txSize := uint32(tx.MsgTx().SerializeSize())
|
// for overflow.
|
||||||
blockPlusTxSize := blockSize + txSize
|
txMass, err := blockdag.CalcTxMass(tx, g.dag.UTXOSet())
|
||||||
if blockPlusTxSize < blockSize ||
|
if err != nil {
|
||||||
blockPlusTxSize >= g.policy.BlockMaxSize {
|
log.Tracef("Skipping tx %s due to error in "+
|
||||||
|
"CalcTxMass: %s", tx.ID(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if blockMass+txMass < blockMass ||
|
||||||
|
blockMass >= g.policy.BlockMaxMass {
|
||||||
log.Tracef("Skipping tx %s because it would exceed "+
|
log.Tracef("Skipping tx %s because it would exceed "+
|
||||||
"the max block size", tx.ID())
|
"the max block mass", tx.ID())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,12 +489,13 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the transaction to the block, increment counters, and
|
// Add the transaction to the block, increment counters, and
|
||||||
// save the fees and signature operation counts to the block
|
// save the masses, fees, and signature operation counts to the block
|
||||||
// template.
|
// template.
|
||||||
blockTxns = append(blockTxns, tx)
|
blockTxns = append(blockTxns, tx)
|
||||||
blockSize += txSize
|
blockMass += txMass
|
||||||
blockSigOps += int64(numSigOps)
|
blockSigOps += numSigOps
|
||||||
totalFees += prioItem.fee
|
totalFees += prioItem.fee
|
||||||
|
txMasses = append(txMasses, txMass)
|
||||||
txFees = append(txFees, prioItem.fee)
|
txFees = append(txFees, prioItem.fee)
|
||||||
txSigOpCounts = append(txSigOpCounts, numSigOps)
|
txSigOpCounts = append(txSigOpCounts, numSigOps)
|
||||||
|
|
||||||
@ -489,12 +503,6 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
prioItem.tx.ID(), prioItem.feePerKB)
|
prioItem.tx.ID(), prioItem.feePerKB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that the actual transactions have been selected, update the
|
|
||||||
// block size for the real transaction count and coinbase value with
|
|
||||||
// the total fees accordingly.
|
|
||||||
blockSize -= wire.MaxVarIntPayload -
|
|
||||||
uint32(wire.VarIntSerializeSize(uint64(len(blockTxns))))
|
|
||||||
|
|
||||||
// Calculate the required difficulty for the block. The timestamp
|
// Calculate the required difficulty for the block. The timestamp
|
||||||
// is potentially adjusted to ensure it comes after the median time of
|
// is potentially adjusted to ensure it comes after the median time of
|
||||||
// the last several blocks per the chain consensus rules.
|
// the last several blocks per the chain consensus rules.
|
||||||
@ -553,12 +561,13 @@ func (g *BlkTmplGenerator) NewBlockTemplate(payToAddress util.Address) (*BlockTe
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Created new block template (%d transactions, %d in fees, "+
|
log.Debugf("Created new block template (%d transactions, %d in fees, "+
|
||||||
"%d signature operations, %d bytes, target difficulty %064x)",
|
"%d signature operations, %d mass, target difficulty %064x)",
|
||||||
len(msgBlock.Transactions), totalFees, blockSigOps, blockSize,
|
len(msgBlock.Transactions), totalFees, blockSigOps, blockMass,
|
||||||
util.CompactToBig(msgBlock.Header.Bits))
|
util.CompactToBig(msgBlock.Header.Bits))
|
||||||
|
|
||||||
return &BlockTemplate{
|
return &BlockTemplate{
|
||||||
Block: &msgBlock,
|
Block: &msgBlock,
|
||||||
|
TxMasses: txMasses,
|
||||||
Fees: txFees,
|
Fees: txFees,
|
||||||
SigOpCounts: txSigOpCounts,
|
SigOpCounts: txSigOpCounts,
|
||||||
ValidPayAddress: payToAddress != nil,
|
ValidPayAddress: payToAddress != nil,
|
||||||
|
@ -89,7 +89,7 @@ func TestNewBlockTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
policy := Policy{
|
policy := Policy{
|
||||||
BlockMaxSize: 50000,
|
BlockMaxMass: 50000,
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we create a block to have coinbase funds for the rest of the test.
|
// First we create a block to have coinbase funds for the rest of the test.
|
||||||
|
@ -8,11 +8,7 @@ package mining
|
|||||||
// the generation of block templates. See the documentation for
|
// the generation of block templates. See the documentation for
|
||||||
// NewBlockTemplate for more details on each of these parameters are used.
|
// NewBlockTemplate for more details on each of these parameters are used.
|
||||||
type Policy struct {
|
type Policy struct {
|
||||||
// BlockMinSize is the minimum block size to be used when generating
|
// BlockMaxMass is the maximum block mass to be used when generating a
|
||||||
// a block template.
|
|
||||||
BlockMinSize uint32
|
|
||||||
|
|
||||||
// BlockMaxSize is the maximum block size to be used when generating a
|
|
||||||
// block template.
|
// block template.
|
||||||
BlockMaxSize uint32
|
BlockMaxMass uint64
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func PrepareBlockForTest(dag *blockdag.BlockDAG, params *dagconfig.Params, paren
|
|||||||
oldVirtual := blockdag.SetVirtualForTest(dag, newVirtual)
|
oldVirtual := blockdag.SetVirtualForTest(dag, newVirtual)
|
||||||
defer blockdag.SetVirtualForTest(dag, oldVirtual)
|
defer blockdag.SetVirtualForTest(dag, oldVirtual)
|
||||||
policy := Policy{
|
policy := Policy{
|
||||||
BlockMaxSize: 50000,
|
BlockMaxMass: 50000,
|
||||||
}
|
}
|
||||||
|
|
||||||
txSource := &fakeTxSource{
|
txSource := &fakeTxSource{
|
||||||
|
@ -1761,6 +1761,7 @@ func (state *gbtWorkState) blockTemplateResult(dag *blockdag.BlockDAG, useCoinba
|
|||||||
Data: hex.EncodeToString(txBuf.Bytes()),
|
Data: hex.EncodeToString(txBuf.Bytes()),
|
||||||
ID: txID.String(),
|
ID: txID.String(),
|
||||||
Depends: depends,
|
Depends: depends,
|
||||||
|
Mass: template.TxMasses[i],
|
||||||
Fee: template.Fees[i],
|
Fee: template.Fees[i],
|
||||||
SigOps: template.SigOpCounts[i],
|
SigOps: template.SigOpCounts[i],
|
||||||
}
|
}
|
||||||
@ -1779,7 +1780,7 @@ func (state *gbtWorkState) blockTemplateResult(dag *blockdag.BlockDAG, useCoinba
|
|||||||
Height: template.Height,
|
Height: template.Height,
|
||||||
ParentHashes: daghash.Strings(header.ParentHashes),
|
ParentHashes: daghash.Strings(header.ParentHashes),
|
||||||
SigOpLimit: blockdag.MaxSigOpsPerBlock,
|
SigOpLimit: blockdag.MaxSigOpsPerBlock,
|
||||||
SizeLimit: wire.MaxBlockPayload,
|
MassLimit: wire.MaxMassPerBlock,
|
||||||
Transactions: transactions,
|
Transactions: transactions,
|
||||||
AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot.String(),
|
AcceptedIDMerkleRoot: header.AcceptedIDMerkleRoot.String(),
|
||||||
UTXOCommitment: header.UTXOCommitment.String(),
|
UTXOCommitment: header.UTXOCommitment.String(),
|
||||||
@ -1822,6 +1823,7 @@ func (state *gbtWorkState) blockTemplateResult(dag *blockdag.BlockDAG, useCoinba
|
|||||||
Data: hex.EncodeToString(txBuf.Bytes()),
|
Data: hex.EncodeToString(txBuf.Bytes()),
|
||||||
ID: tx.TxID().String(),
|
ID: tx.TxID().String(),
|
||||||
Depends: []int64{},
|
Depends: []int64{},
|
||||||
|
Mass: template.TxMasses[0],
|
||||||
Fee: template.Fees[0],
|
Fee: template.Fees[0],
|
||||||
SigOps: template.SigOpCounts[0],
|
SigOps: template.SigOpCounts[0],
|
||||||
}
|
}
|
||||||
@ -2028,8 +2030,8 @@ func chainErrToGBTErrString(err error) string {
|
|||||||
switch ruleErr.ErrorCode {
|
switch ruleErr.ErrorCode {
|
||||||
case blockdag.ErrDuplicateBlock:
|
case blockdag.ErrDuplicateBlock:
|
||||||
return "duplicate"
|
return "duplicate"
|
||||||
case blockdag.ErrBlockTooBig:
|
case blockdag.ErrBlockMassTooHigh:
|
||||||
return "bad-blk-length"
|
return "bad-blk-mass"
|
||||||
case blockdag.ErrBlockVersionTooOld:
|
case blockdag.ErrBlockVersionTooOld:
|
||||||
return "bad-version"
|
return "bad-version"
|
||||||
case blockdag.ErrInvalidTime:
|
case blockdag.ErrInvalidTime:
|
||||||
@ -2054,8 +2056,8 @@ func chainErrToGBTErrString(err error) string {
|
|||||||
return "bad-txns-none"
|
return "bad-txns-none"
|
||||||
case blockdag.ErrNoTxInputs:
|
case blockdag.ErrNoTxInputs:
|
||||||
return "bad-txns-noinputs"
|
return "bad-txns-noinputs"
|
||||||
case blockdag.ErrTxTooBig:
|
case blockdag.ErrTxMassTooHigh:
|
||||||
return "bad-txns-size"
|
return "bad-txns-mass"
|
||||||
case blockdag.ErrBadTxOutValue:
|
case blockdag.ErrBadTxOutValue:
|
||||||
return "bad-txns-outputvalue"
|
return "bad-txns-outputvalue"
|
||||||
case blockdag.ErrDuplicateTxInputs:
|
case blockdag.ErrDuplicateTxInputs:
|
||||||
|
@ -283,7 +283,7 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"templateRequest-capabilities": "List of capabilities",
|
"templateRequest-capabilities": "List of capabilities",
|
||||||
"templateRequest-longPollId": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ",
|
"templateRequest-longPollId": "The long poll ID of a job to monitor for expiration; required and valid only for long poll requests ",
|
||||||
"templateRequest-sigOpLimit": "Number of signature operations allowed in blocks (this parameter is ignored)",
|
"templateRequest-sigOpLimit": "Number of signature operations allowed in blocks (this parameter is ignored)",
|
||||||
"templateRequest-sizeLimit": "Number of bytes allowed in blocks (this parameter is ignored)",
|
"templateRequest-massLimit": "Max transaction mass allowed in blocks (this parameter is ignored)",
|
||||||
"templateRequest-maxVersion": "Highest supported block version number (this parameter is ignored)",
|
"templateRequest-maxVersion": "Highest supported block version number (this parameter is ignored)",
|
||||||
"templateRequest-target": "The desired target for the block template (this parameter is ignored)",
|
"templateRequest-target": "The desired target for the block template (this parameter is ignored)",
|
||||||
"templateRequest-data": "Hex-encoded block data (only for mode=proposal)",
|
"templateRequest-data": "Hex-encoded block data (only for mode=proposal)",
|
||||||
@ -294,6 +294,7 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getBlockTemplateResultTx-hash": "Hex-encoded transaction hash (little endian if treated as a 256-bit number)",
|
"getBlockTemplateResultTx-hash": "Hex-encoded transaction hash (little endian if treated as a 256-bit number)",
|
||||||
"getBlockTemplateResultTx-id": "Hex-encoded transaction ID (little endian if treated as a 256-bit number)",
|
"getBlockTemplateResultTx-id": "Hex-encoded transaction ID (little endian if treated as a 256-bit number)",
|
||||||
"getBlockTemplateResultTx-depends": "Other transactions before this one (by 1-based index in the 'transactions' list) that must be present in the final block if this one is",
|
"getBlockTemplateResultTx-depends": "Other transactions before this one (by 1-based index in the 'transactions' list) that must be present in the final block if this one is",
|
||||||
|
"getBlockTemplateResultTx-mass": "Total mass of all transactions in the block",
|
||||||
"getBlockTemplateResultTx-fee": "Difference in value between transaction inputs and outputs (in Satoshi)",
|
"getBlockTemplateResultTx-fee": "Difference in value between transaction inputs and outputs (in Satoshi)",
|
||||||
"getBlockTemplateResultTx-sigOps": "Total number of signature operations as counted for purposes of block limits",
|
"getBlockTemplateResultTx-sigOps": "Total number of signature operations as counted for purposes of block limits",
|
||||||
|
|
||||||
@ -305,8 +306,8 @@ var helpDescsEnUS = map[string]string{
|
|||||||
"getBlockTemplateResult-curTime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules",
|
"getBlockTemplateResult-curTime": "Current time as seen by the server (recommended for block time); must fall within mintime/maxtime rules",
|
||||||
"getBlockTemplateResult-height": "Height of the block to be solved",
|
"getBlockTemplateResult-height": "Height of the block to be solved",
|
||||||
"getBlockTemplateResult-parentHashes": "Hex-encoded big-endian hashes of the parent blocks",
|
"getBlockTemplateResult-parentHashes": "Hex-encoded big-endian hashes of the parent blocks",
|
||||||
"getBlockTemplateResult-sigOpLimit": "Number of sigops allowed in blocks ",
|
"getBlockTemplateResult-sigOpLimit": "Number of sigops allowed in blocks",
|
||||||
"getBlockTemplateResult-sizeLimit": "Number of bytes allowed in blocks",
|
"getBlockTemplateResult-massLimit": "Max transaction mass allowed in blocks",
|
||||||
"getBlockTemplateResult-transactions": "Array of transactions as JSON objects",
|
"getBlockTemplateResult-transactions": "Array of transactions as JSON objects",
|
||||||
"getBlockTemplateResult-acceptedIdMerkleRoot": "The root of the merkle tree of transaction IDs accepted by this block",
|
"getBlockTemplateResult-acceptedIdMerkleRoot": "The root of the merkle tree of transaction IDs accepted by this block",
|
||||||
"getBlockTemplateResult-utxoCommitment": "An ECMH UTXO commitment of this block",
|
"getBlockTemplateResult-utxoCommitment": "An ECMH UTXO commitment of this block",
|
||||||
|
@ -101,8 +101,7 @@ func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params
|
|||||||
// NOTE: The CPU miner relies on the mempool, so the mempool has to be
|
// NOTE: The CPU miner relies on the mempool, so the mempool has to be
|
||||||
// created before calling the function to create the CPU miner.
|
// created before calling the function to create the CPU miner.
|
||||||
policy := mining.Policy{
|
policy := mining.Policy{
|
||||||
BlockMinSize: cfg.BlockMinSize,
|
BlockMaxMass: cfg.BlockMaxMass,
|
||||||
BlockMaxSize: cfg.BlockMaxSize,
|
|
||||||
}
|
}
|
||||||
blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy,
|
blockTemplateGenerator := mining.NewBlkTmplGenerator(&policy,
|
||||||
s.p2pServer.DAGParams, s.p2pServer.TxMemPool, s.p2pServer.DAG, s.p2pServer.TimeSource, s.p2pServer.SigCache)
|
s.p2pServer.DAGParams, s.p2pServer.TxMemPool, s.p2pServer.DAG, s.p2pServer.TimeSource, s.p2pServer.SigCache)
|
||||||
|
@ -21,12 +21,12 @@ import (
|
|||||||
// backing array multiple times.
|
// backing array multiple times.
|
||||||
const defaultTransactionAlloc = 2048
|
const defaultTransactionAlloc = 2048
|
||||||
|
|
||||||
// MaxBlockPayload is the maximum bytes a block message can be in bytes.
|
// maxMassPerBlock is the maximum total transaction mass a block may contain.
|
||||||
const MaxBlockPayload = 1000000
|
const MaxMassPerBlock = 10000000
|
||||||
|
|
||||||
// maxTxPerBlock is the maximum number of transactions that could
|
// maxTxPerBlock is the maximum number of transactions that could
|
||||||
// possibly fit into a block.
|
// possibly fit into a block.
|
||||||
const maxTxPerBlock = (MaxBlockPayload / minTxPayload) + 1
|
const maxTxPerBlock = (MaxMassPerBlock / minTxPayload) + 1
|
||||||
|
|
||||||
// TxLoc holds locator data for the offset and length of where a transaction is
|
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||||
// located within a MsgBlock data buffer.
|
// located within a MsgBlock data buffer.
|
||||||
@ -217,10 +217,7 @@ func (msg *MsgBlock) Command() string {
|
|||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
// receiver. This is part of the Message interface implementation.
|
// receiver. This is part of the Message interface implementation.
|
||||||
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
// Block header at 80 bytes + transaction count + max transactions
|
return MaxMessagePayload
|
||||||
// which can vary up to the MaxBlockPayload (including the block header
|
|
||||||
// and transaction count).
|
|
||||||
return MaxBlockPayload
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockHash computes the block identifier hash for this block.
|
// BlockHash computes the block identifier hash for this block.
|
||||||
|
@ -40,8 +40,7 @@ func TestBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
// Num addresses (varInt) + max allowed addresses.
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
wantPayload := uint32(1000000)
|
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
if maxPayload != wantPayload {
|
if maxPayload != wantPayload {
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
@ -133,7 +133,7 @@ func (msg *MsgMerkleBlock) Command() string {
|
|||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
// receiver. This is part of the Message interface implementation.
|
// receiver. This is part of the Message interface implementation.
|
||||||
func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
|
func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
return MaxBlockPayload
|
return MaxMessagePayload
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
|
// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
|
||||||
|
@ -38,8 +38,7 @@ func TestMerkleBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
// Num addresses (varInt) + max allowed addresses.
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
wantPayload := uint32(1000000)
|
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
if maxPayload != wantPayload {
|
if maxPayload != wantPayload {
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
@ -765,7 +765,7 @@ func (msg *MsgTx) Command() string {
|
|||||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
// receiver. This is part of the Message interface implementation.
|
// receiver. This is part of the Message interface implementation.
|
||||||
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
|
func (msg *MsgTx) MaxPayloadLength(pver uint32) uint32 {
|
||||||
return MaxBlockPayload
|
return MaxMessagePayload
|
||||||
}
|
}
|
||||||
|
|
||||||
// PkScriptLocs returns a slice containing the start of each public key script
|
// PkScriptLocs returns a slice containing the start of each public key script
|
||||||
|
@ -37,7 +37,7 @@ func TestTx(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure max payload is expected value for latest protocol version.
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
wantPayload := uint32(1000 * 1000)
|
wantPayload := uint32(1024 * 1024 * 32)
|
||||||
maxPayload := msg.MaxPayloadLength(pver)
|
maxPayload := msg.MaxPayloadLength(pver)
|
||||||
if maxPayload != wantPayload {
|
if maxPayload != wantPayload {
|
||||||
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
t.Errorf("MaxPayloadLength: wrong max payload length for "+
|
||||||
|
Loading…
x
Reference in New Issue
Block a user