Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BIPs 141, 142, 143, 145 and 147 (Segregated Witness) #656

Merged
merged 33 commits into from Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
782aa02
BIP0144+wire: introduce the new SFNodeWitness service bit
Roasbeef Oct 18, 2016
27b7fa9
BIP0144+wire: add a MessageEncoding variant for serialization/deseria…
Roasbeef Oct 18, 2016
8fe7dda
BIP0144+wire: introduce new segregated witness inventory types
Roasbeef Oct 18, 2016
b2efc18
BIP0144+wire: implement witness encoding/decoding for transactions
Roasbeef Oct 18, 2016
5b6e9f8
BIP0144+peer: specify the wire encoding type when reading/writing mes…
Roasbeef Oct 18, 2016
6ad38fd
BIP0144: properly fetch witness data from witness-enabled peers
Roasbeef Oct 18, 2016
efb0fc1
BIP0143+txscript: add segwit sighash, signing, and HashCache integration
Roasbeef Oct 19, 2016
83c673c
BIP0141+txscript: awareness of new standard script templates, add hel…
Roasbeef Oct 19, 2016
b09e038
BIP0141+txscript: implement signature operation cost calculations
Roasbeef Oct 19, 2016
7ad355f
txscript: fix off-by-one error due to new OP_CODESEPARATOR behavior i…
Roasbeef Oct 19, 2016
51506d2
BIP0141+txscript: implement witness program validation
Roasbeef Oct 19, 2016
40a5b21
BIP 147: enforce NULLDUMMY w/ segwit verification
Roasbeef Oct 19, 2016
2b928de
btcec: add new IsCompressedPubKey function
Roasbeef Apr 26, 2017
064b869
txscript: add verification of the post-segwit minimal if policy
Roasbeef Apr 26, 2017
9984ea6
txscript: add verification of the post-segwit pub key type constraint
Roasbeef Apr 26, 2017
2d4f350
txscript: add new post segwit policies to standard script flags
Roasbeef Apr 26, 2017
1a55439
txscript: convert all new segwit error types to ErrorCode's
Roasbeef Apr 26, 2017
9be93bc
txscript: update reference tests to include new segwit related cases
Roasbeef Oct 19, 2016
9b6f519
BIP0141+wire: add a WitnessHash method to tx
Roasbeef Oct 19, 2016
181dc4e
chaincfg: add address bytes for p2wsh and p2wkh addresses
Roasbeef Oct 19, 2016
73428ee
BIP0141+blockchain: implement tx/block weight calculation funcitons
Roasbeef Oct 19, 2016
fc9dd8a
BIP0141+blockchain: add functions for extracting and validating witne…
Roasbeef Oct 19, 2016
1972ff3
BIP0141+blockchain: implement segwit block validation rules
Roasbeef Oct 19, 2016
dac46ff
blockchain/indexers: add addrindex support for p2wkh and p2wsh
Roasbeef Oct 19, 2016
62e8982
mempool: modify mempool sanity checks to be segwit aware
Roasbeef Oct 19, 2016
dbc262c
btcjson: update RPC calls to return segwit related data
Roasbeef Oct 19, 2016
3ba4285
mining+config: modify GBT mining to limit by weight, add witness comm…
Roasbeef Oct 19, 2016
65060a3
chaincfg+integration: add BIP0009 deployment parameters for segwit
Roasbeef Jan 4, 2017
83a27be
blockchain: guard enforcement of segwit rules by a BIP0009 state check
Roasbeef Jan 4, 2017
a8f41fa
mining: update GBT generation to include witness commitment
Roasbeef Jan 4, 2017
6d9d18c
blockchain: only populate hashcache if tx is segwitty
Roasbeef Mar 9, 2017
c9d1a8c
integration/rpctest: update API usage due to segwit
Roasbeef May 11, 2017
a925475
build: update glide.lock to target latest btcutil commit
Roasbeef Jul 31, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion addrmgr/addrmanager.go
Expand Up @@ -1073,7 +1073,8 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
} else {
ip = net.IPv4zero
}
bestAddress = wire.NewNetAddressIPPort(ip, 0, wire.SFNodeNetwork)
services := wire.SFNodeNetwork | wire.SFNodeWitness | wire.SFNodeBloom
bestAddress = wire.NewNetAddressIPPort(ip, 0, services)
}

return bestAddress
Expand Down
56 changes: 37 additions & 19 deletions blockchain/chain.go
Expand Up @@ -42,25 +42,29 @@ type orphanBlock struct {
// However, the returned snapshot must be treated as immutable since it is
// shared by all callers.
type BestState struct {
Hash chainhash.Hash // The hash of the block.
Height int32 // The height of the block.
Bits uint32 // The difficulty bits of the block.
BlockSize uint64 // The size of the block.
NumTxns uint64 // The number of txns in the block.
TotalTxns uint64 // The total number of txns in the chain.
MedianTime time.Time // Median time as per CalcPastMedianTime.
Hash chainhash.Hash // The hash of the block.
Height int32 // The height of the block.
Bits uint32 // The difficulty bits of the block.
BlockSize uint64 // The size of the block.
BlockWeight uint64 // The weight of the block.
NumTxns uint64 // The number of txns in the block.
TotalTxns uint64 // The total number of txns in the chain.
MedianTime time.Time // Median time as per CalcPastMedianTime.
}

// newBestState returns a new best stats instance for the given parameters.
func newBestState(node *blockNode, blockSize, numTxns, totalTxns uint64, medianTime time.Time) *BestState {
func newBestState(node *blockNode, blockSize, blockWeight, numTxns,
totalTxns uint64, medianTime time.Time) *BestState {

return &BestState{
Hash: node.hash,
Height: node.height,
Bits: node.bits,
BlockSize: blockSize,
NumTxns: numTxns,
TotalTxns: totalTxns,
MedianTime: medianTime,
Hash: node.hash,
Height: node.height,
Bits: node.bits,
BlockSize: blockSize,
BlockWeight: blockWeight,
NumTxns: numTxns,
TotalTxns: totalTxns,
MedianTime: medianTime,
}
}

Expand All @@ -80,6 +84,7 @@ type BlockChain struct {
notifications NotificationCallback
sigCache *txscript.SigCache
indexManager IndexManager
hashCache *txscript.HashCache

// The following fields are calculated based upon the provided chain
// parameters. They are also set when the instance is created and
Expand Down Expand Up @@ -616,8 +621,9 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
b.stateLock.RUnlock()
numTxns := uint64(len(block.MsgBlock().Transactions))
blockSize := uint64(block.MsgBlock().SerializeSize())
state := newBestState(node, blockSize, numTxns, curTotalTxns+numTxns,
medianTime)
blockWeight := uint64(GetBlockWeight(block))
state := newBestState(node, blockSize, blockWeight, numTxns,
curTotalTxns+numTxns, medianTime)

// Atomically insert info into the database.
err = b.db.Update(func(dbTx database.Tx) error {
Expand Down Expand Up @@ -744,9 +750,10 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view
b.stateLock.RUnlock()
numTxns := uint64(len(prevBlock.MsgBlock().Transactions))
blockSize := uint64(prevBlock.MsgBlock().SerializeSize())
blockWeight := uint64(GetBlockWeight(prevBlock))
newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions))
state := newBestState(prevNode, blockSize, numTxns, newTotalTxns,
medianTime)
state := newBestState(prevNode, blockSize, blockWeight, numTxns,
newTotalTxns, medianTime)

err = b.db.Update(func(dbTx database.Tx) error {
// Update best block state.
Expand Down Expand Up @@ -1328,6 +1335,16 @@ type Config struct {
// This field can be nil if the caller does not wish to make use of an
// index manager.
IndexManager IndexManager

// HashCache defines a transaction hash mid-state cache to use when
// validating transactions. This cache has the potential to greatly
// speed up transaction validation as re-using the pre-calculated
// mid-state eliminates the O(N^2) validation complexity due to the
// SigHashAll flag.
//
// This field can be nil if the caller is not interested in using a
// signature cache.
HashCache *txscript.HashCache
}

// New returns a BlockChain instance using the provided configuration details.
Expand Down Expand Up @@ -1378,6 +1395,7 @@ func New(config *Config) (*BlockChain, error) {
maxRetargetTimespan: targetTimespan * adjustmentFactor,
blocksPerRetarget: int32(targetTimespan / targetTimePerBlock),
index: newBlockIndex(config.DB, params),
hashCache: config.HashCache,
orphans: make(map[chainhash.Hash]*orphanBlock),
prevOrphans: make(map[chainhash.Hash][]*orphanBlock),
warningCaches: newThresholdCaches(vbNumBits),
Expand Down
12 changes: 7 additions & 5 deletions blockchain/chainio.go
Expand Up @@ -1092,8 +1092,9 @@ func (b *BlockChain) createChainState() error {
// genesis block, use its timestamp for the median time.
numTxns := uint64(len(genesisBlock.MsgBlock().Transactions))
blockSize := uint64(genesisBlock.MsgBlock().SerializeSize())
b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns, numTxns,
time.Unix(b.bestNode.timestamp, 0))
blockWeight := uint64(GetBlockWeight(genesisBlock))
b.stateSnapshot = newBestState(b.bestNode, blockSize, blockWeight,
numTxns, numTxns, time.Unix(b.bestNode.timestamp, 0))

// Create the initial the database chain state including creating the
// necessary index buckets and inserting the genesis block.
Expand Down Expand Up @@ -1197,11 +1198,12 @@ func (b *BlockChain) initChainState() error {

// Initialize the state related to the best block.
blockSize := uint64(len(blockBytes))
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
numTxns := uint64(len(block.Transactions))
b.stateSnapshot = newBestState(b.bestNode, blockSize, numTxns,
state.totalTxns, medianTime)

b.stateSnapshot = newBestState(b.bestNode, blockSize, blockWeight,
numTxns, state.totalTxns, medianTime)
isStateInitialized = true

return nil
})
if err != nil {
Expand Down
98 changes: 60 additions & 38 deletions blockchain/error.go
Expand Up @@ -41,6 +41,10 @@ const (
// maximum allowed size.
ErrBlockTooBig

// ErrBlockWeightTooHigh indicates that the block's computed weight
// metric exceeds the maximum allowed value.
ErrBlockWeightTooHigh

// ErrBlockVersionTooOld indicates the block version is too old and is
// no longer accepted since the majority of the network has upgraded
// to a newer version.
Expand Down Expand Up @@ -198,48 +202,66 @@ const (
// such signature verification failures and execution past the end of
// the stack.
ErrScriptValidation

// ErrUnexpectedWitness indicates that a block includes transactions
// with witness data, but doesn't also have a witness commitment within
// the coinbase transaction.
ErrUnexpectedWitness

// ErrInvalidWitnessCommitment indicates that a block's witness
// commitment is not well formed.
ErrInvalidWitnessCommitment

// ErrWitnessCommitmentMismatch indicates that the witness commitment
// included in the block's coinbase transaction doesn't match the
// manually computed witness commitment.
ErrWitnessCommitmentMismatch
)

// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrDuplicateBlock: "ErrDuplicateBlock",
ErrBlockTooBig: "ErrBlockTooBig",
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
ErrInvalidTime: "ErrInvalidTime",
ErrTimeTooOld: "ErrTimeTooOld",
ErrTimeTooNew: "ErrTimeTooNew",
ErrDifficultyTooLow: "ErrDifficultyTooLow",
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
ErrHighHash: "ErrHighHash",
ErrBadMerkleRoot: "ErrBadMerkleRoot",
ErrBadCheckpoint: "ErrBadCheckpoint",
ErrForkTooOld: "ErrForkTooOld",
ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld",
ErrNoTransactions: "ErrNoTransactions",
ErrTooManyTransactions: "ErrTooManyTransactions",
ErrNoTxInputs: "ErrNoTxInputs",
ErrNoTxOutputs: "ErrNoTxOutputs",
ErrTxTooBig: "ErrTxTooBig",
ErrBadTxOutValue: "ErrBadTxOutValue",
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
ErrBadTxInput: "ErrBadTxInput",
ErrMissingTx: "ErrMissingTx",
ErrUnfinalizedTx: "ErrUnfinalizedTx",
ErrDuplicateTx: "ErrDuplicateTx",
ErrOverwriteTx: "ErrOverwriteTx",
ErrImmatureSpend: "ErrImmatureSpend",
ErrDoubleSpend: "ErrDoubleSpend",
ErrSpendTooHigh: "ErrSpendTooHigh",
ErrBadFees: "ErrBadFees",
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
ErrMissingCoinbaseHeight: "ErrMissingCoinbaseHeight",
ErrBadCoinbaseHeight: "ErrBadCoinbaseHeight",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrDuplicateBlock: "ErrDuplicateBlock",
ErrBlockTooBig: "ErrBlockTooBig",
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
ErrBlockWeightTooHigh: "ErrBlockWeightTooHigh",
ErrInvalidTime: "ErrInvalidTime",
ErrTimeTooOld: "ErrTimeTooOld",
ErrTimeTooNew: "ErrTimeTooNew",
ErrDifficultyTooLow: "ErrDifficultyTooLow",
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
ErrHighHash: "ErrHighHash",
ErrBadMerkleRoot: "ErrBadMerkleRoot",
ErrBadCheckpoint: "ErrBadCheckpoint",
ErrForkTooOld: "ErrForkTooOld",
ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld",
ErrNoTransactions: "ErrNoTransactions",
ErrTooManyTransactions: "ErrTooManyTransactions",
ErrNoTxInputs: "ErrNoTxInputs",
ErrNoTxOutputs: "ErrNoTxOutputs",
ErrTxTooBig: "ErrTxTooBig",
ErrBadTxOutValue: "ErrBadTxOutValue",
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
ErrBadTxInput: "ErrBadTxInput",
ErrMissingTx: "ErrMissingTx",
ErrUnfinalizedTx: "ErrUnfinalizedTx",
ErrDuplicateTx: "ErrDuplicateTx",
ErrOverwriteTx: "ErrOverwriteTx",
ErrImmatureSpend: "ErrImmatureSpend",
ErrDoubleSpend: "ErrDoubleSpend",
ErrSpendTooHigh: "ErrSpendTooHigh",
ErrBadFees: "ErrBadFees",
ErrTooManySigOps: "ErrTooManySigOps",
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
ErrMultipleCoinbases: "ErrMultipleCoinbases",
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
ErrMissingCoinbaseHeight: "ErrMissingCoinbaseHeight",
ErrBadCoinbaseHeight: "ErrBadCoinbaseHeight",
ErrScriptMalformed: "ErrScriptMalformed",
ErrScriptValidation: "ErrScriptValidation",
ErrUnexpectedWitness: "ErrUnexpectedWitness",
ErrInvalidWitnessCommitment: "ErrInvalidWitnessCommitment",
ErrWitnessCommitmentMismatch: "ErrWitnessCommitmentMismatch",
}

// String returns the ErrorCode as a human-readable name.
Expand Down
2 changes: 1 addition & 1 deletion blockchain/fullblocks_test.go
Expand Up @@ -117,7 +117,7 @@ func TestFullBlocks(t *testing.T) {

// Ensure there is an error due to deserializing the block.
var msgBlock wire.MsgBlock
err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0)
err := msgBlock.BtcDecode(bytes.NewReader(item.RawBlock), 0, wire.BaseEncoding)
if _, ok := err.(*wire.MessageError); !ok {
t.Fatalf("block %q (hash %s, height %d) should have "+
"failed to decode", item.Name, blockHash,
Expand Down
6 changes: 3 additions & 3 deletions blockchain/fullblocktests/generate.go
Expand Up @@ -309,7 +309,7 @@ func calcMerkleRoot(txns []*wire.MsgTx) chainhash.Hash {
for _, tx := range txns {
utilTxns = append(utilTxns, btcutil.NewTx(tx))
}
merkles := blockchain.BuildMerkleTreeStore(utilTxns)
merkles := blockchain.BuildMerkleTreeStore(utilTxns, false)
return *merkles[len(merkles)-1]
}

Expand Down Expand Up @@ -635,10 +635,10 @@ func nonCanonicalVarInt(val uint32) []byte {
// encoding.
func encodeNonCanonicalBlock(b *wire.MsgBlock) []byte {
var buf bytes.Buffer
b.Header.BtcEncode(&buf, 0)
b.Header.BtcEncode(&buf, 0, wire.BaseEncoding)
buf.Write(nonCanonicalVarInt(uint32(len(b.Transactions))))
for _, tx := range b.Transactions {
tx.BtcEncode(&buf, 0)
tx.BtcEncode(&buf, 0, wire.BaseEncoding)
}
return buf.Bytes()
}
Expand Down
30 changes: 30 additions & 0 deletions blockchain/indexers/addrindex.go
Expand Up @@ -51,6 +51,18 @@ const (
// hash.
addrKeyTypeScriptHash = 1

// addrKeyTypePubKeyHash is the address type in an address key which
// represents a pay-to-witness-pubkey-hash address. This is required
// as the 20-byte data push of a p2wkh witness program may be the same
// data push used a p2pkh address.
addrKeyTypeWitnessPubKeyHash = 2

// addrKeyTypeScriptHash is the address type in an address key which
// represents a pay-to-witness-script-hash address. This is required,
// as p2wsh are distinct from p2sh addresses since they use a new
// script template, as well as a 32-byte data push.
addrKeyTypeWitnessScriptHash = 3

// Size of a transaction entry. It consists of 4 bytes block id + 4
// bytes offset + 4 bytes length.
txEntrySize = 4 + 4 + 4
Expand Down Expand Up @@ -534,6 +546,24 @@ func addrToKey(addr btcutil.Address) ([addrKeySize]byte, error) {
result[0] = addrKeyTypePubKeyHash
copy(result[1:], addr.AddressPubKeyHash().Hash160()[:])
return result, nil

case *btcutil.AddressWitnessScriptHash:
var result [addrKeySize]byte
result[0] = addrKeyTypeWitnessScriptHash

// P2WSH outputs utilize a 32-byte data push created by hashing
// the script with sha256 instead of hash160. In order to keep
// all address entries within the database uniform and compact,
// we use a hash160 here to reduce the size of the salient data
// push to 20-bytes.
copy(result[1:], btcutil.Hash160(addr.ScriptAddress()))
return result, nil

case *btcutil.AddressWitnessPubKeyHash:
var result [addrKeySize]byte
result[0] = addrKeyTypeWitnessPubKeyHash
copy(result[1:], addr.Hash160()[:])
return result, nil
}

return [addrKeySize]byte{}, errUnsupportedAddressType
Expand Down