dcrd/txscript/consensus.go
Dave Collins ce852eb852
blockchain: Do coinbase nulldata check locally.
This refactors the consensus code which extracts the null data from the
coinbase from txscript.ExtractCoinbaseNullData so that it is performed
directly in the validation code where it more properly belongs.

The only reason the extraction was previously done in txscript is
because it was not possible to parse scripts outside of it, but that is
no longer the case now that txscript offers an exported tokenizer for
that purpose.

The extraction code is ever so slightly more efficient now that it no
longer needs to be as generic since it now has direct knowledge of the
conditions that need to be handled.

Great care was taken to ensure the semantics are not changed while
refactoring the code and no additional tests are added in this commit
because all of the conditions and code paths are covered by the tests
recently added to the full block tests.

While here, also perform some related code cleanup in the function and
improve the error messages .

Since the txscript.ExtractCoinbaseNullData is no longer necessary, this
deprecates the function and releated error code and constant so they can
be removed in the next major version of txscript.

Finally, since this relies on the script tokenizer which is not yet in a
released version of the txscript module, bump the requirement to include
an as yet unreleased version of txscript to ensure the next time the
blockchain module is released, it will require a newer version of
txscript to be released first.
2019-06-19 13:26:33 -05:00

68 lines
2.6 KiB
Go

// Copyright (c) 2015-2016 The btcsuite developers
// Copyright (c) 2015-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package txscript
import (
"fmt"
)
const (
// LockTimeThreshold is the number below which a lock time is
// interpreted to be a block number. Since an average of one block
// is generated per 10 minutes, this allows blocks for about 9,512
// years.
LockTimeThreshold = 5e8 // Tue Nov 5 00:53:20 1985 UTC
// maxUniqueCoinbaseNullDataSize is the maximum number of bytes allowed
// in the pushed data output of the coinbase output that is used to
// ensure the coinbase has a unique hash.
//
// Deprecated: This will be removed in the next major version bump.
maxUniqueCoinbaseNullDataSize = 256
)
// ExtractCoinbaseNullData ensures the passed script is a nulldata script as
// required by the consensus rules for the coinbase output that is used to
// ensure the coinbase has a unique hash and returns the data it pushes.
//
// NOTE: This function is only valid for version 0 scripts. Since the function
// does not accept a script version, the results are undefined for other script
// versions.
//
// Deprecated: This will be removed in the next major version bump.
func ExtractCoinbaseNullData(pkScript []byte) ([]byte, error) {
// The nulldata in the coinbase must be a single OP_RETURN followed by a
// data push up to maxUniqueCoinbaseNullDataSize bytes.
//
// NOTE: This is intentionally not using GetScriptClass and the related
// functions because those are specifically for standardness checks which
// can change over time and this function is specifically intended to be
// used by the consensus rules.
//
// Also of note is that technically normal nulldata scripts support encoding
// numbers via small opcodes, however the consensus rules require the block
// height to be encoded as a 4-byte little-endian uint32 pushed via a normal
// data push, as opposed to using the normal number handling semantics of
// scripts, so this is specialized to accommodate that.
const scriptVersion = 0
if len(pkScript) == 1 && pkScript[0] == OP_RETURN {
return nil, nil
}
if len(pkScript) > 1 && pkScript[0] == OP_RETURN {
tokenizer := MakeScriptTokenizer(scriptVersion, pkScript[1:])
if tokenizer.Next() && tokenizer.Done() &&
tokenizer.Opcode() <= OP_PUSHDATA4 &&
len(tokenizer.Data()) <= maxUniqueCoinbaseNullDataSize {
return tokenizer.Data(), nil
}
}
str := fmt.Sprintf("script %x is not well-formed coinbase nulldata",
pkScript)
return nil, scriptError(ErrMalformedCoinbaseNullData, str)
}