diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0d760cb --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2013 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 761ef82..71cd0c5 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,54 @@ btcutil ======= Package btcutil provides bitcoin-specific convenience functions and types. +The test coverage is currently ~76%, however it will improved to 100% in the +near future. See `test_coverage.txt` for the gocov coverage report. +Alternatively, if you are running a POSIX OS, you can run the `cov_report.sh` +script for a real-time report. Package btcutil is licensed under the liberal +ISC license. + +This package was developed for btcd, an alternative full-node implementation of +bitcoin which is under active development by Conformal. Although it was +primarily written for btcd, this package has intentionally been designed so it +can be used as a standalone package for any projects needing the functionality +provided. + +## Documentation + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/conformal/btcutil + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/conformal/btcutil + +## Installation + +```bash +$ go get github.com/conformal/btcutil +``` + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from Conformal. To verify the +signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package btcutil is licensed under the liberal ISC License. diff --git a/block.go b/block.go new file mode 100644 index 0000000..e857d18 --- /dev/null +++ b/block.go @@ -0,0 +1,226 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcutil + +import ( + "bytes" + "fmt" + "github.com/conformal/btcwire" +) + +// OutOfRangeError describes an error due to accessing an element that is out +// of range. +type OutOfRangeError string + +// BlockHeightUnknown is the value returned for a block height that is unknown. +// This is typically because the block has not been inserted into the main chain +// yet. +const BlockHeightUnknown = int64(-1) + +// Error satisfies the error interface and prints human-readable errors. +func (e OutOfRangeError) Error() string { + return string(e) +} + +// Block defines a bitcoin block that provides easier and more efficient +// manipulation of raw wire protocol blocks. It also memoizes hashes for the +// block and its transactions on their first access so subsequent accesses don't +// have to repeat the relatively expensive hashing operations. +type Block struct { + msgBlock *btcwire.MsgBlock // Underlying MsgBlock + rawBlock []byte // Raw wire encoded bytes for the block + protocolVersion uint32 // Protocol version used to encode rawBlock + blockSha *btcwire.ShaHash // Cached block hash + blockHeight int64 // Height in the main block chain + txShas []*btcwire.ShaHash // Cached transaction hashes + txShasGenerated bool // ALL transaction hashes generated +} + +// MsgBlock returns the underlying btcwire.MsgBlock for the Block. +func (b *Block) MsgBlock() *btcwire.MsgBlock { + // Return the cached block. + return b.msgBlock +} + +// Bytes returns the raw wire protocol encoded bytes for the Block and the +// protocol version used to encode it. This is equivalent to calling BtcEncode +// on the underlying btcwire.MsgBlock, however it caches the result so +// subsequent calls are more efficient. +func (b *Block) Bytes() ([]byte, uint32, error) { + // Return the cached raw block bytes and associated protocol version if + // it has already been generated. + if len(b.rawBlock) != 0 { + return b.rawBlock, b.protocolVersion, nil + } + + // Encode the MsgBlock into raw block bytes. + var w bytes.Buffer + err := b.msgBlock.BtcEncode(&w, b.protocolVersion) + if err != nil { + return nil, 0, err + } + rawBlock := w.Bytes() + + // Cache the encoded bytes and return them. + b.rawBlock = rawBlock + return rawBlock, b.protocolVersion, nil +} + +// Sha returns the block identifier hash for the Block. This is equivalent to +// calling BlockSha on the underlying btcwire.MsgBlock, however it caches the +// result so subsequent calls are more efficient. +func (b *Block) Sha() (*btcwire.ShaHash, error) { + // Return the cached block hash if it has already been generated. + if b.blockSha != nil { + return b.blockSha, nil + } + + // Generate the block hash. Ignore the error since BlockSha can't + // currently fail. + sha, _ := b.msgBlock.BlockSha(b.protocolVersion) + + // Cache the block hash and return it. + b.blockSha = &sha + return &sha, nil +} + +// TxSha returns the hash for the requested transaction number in the Block. +// The supplied index is 0 based. That is to say, the first transaction is the +// block is txNum 0. This is equivalent to calling TxSha on the underlying +// btcwire.MsgTx, however it caches the result so subsequent calls are more +// efficient. +func (b *Block) TxSha(txNum int) (*btcwire.ShaHash, error) { + // Ensure the requested transaction is in range. + numTx := b.msgBlock.Header.TxnCount + if txNum < 0 || uint64(txNum) > numTx { + str := fmt.Sprintf("transaction index %d is out of range - max %d", + txNum, numTx-1) + return nil, OutOfRangeError(str) + } + + // Generate slice to hold all of the transaction hashes if needed. + if len(b.txShas) == 0 { + b.txShas = make([]*btcwire.ShaHash, numTx) + } + + // Return the cached hash if it has already been generated. + if b.txShas[txNum] != nil { + return b.txShas[txNum], nil + } + + // Generate the hash for the transaction. Ignore the error since TxSha + // can't currently fail. + sha, _ := b.msgBlock.Transactions[txNum].TxSha(b.protocolVersion) + + // Cache the transaction hash and return it. + b.txShas[txNum] = &sha + return &sha, nil +} + +// TxShas returns a slice of hashes for all transactions in the Block. This is +// equivalent to calling TxSha on each underlying btcwire.MsgTx, however it +// caches the result so subsequent calls are more efficient. +func (b *Block) TxShas() ([]*btcwire.ShaHash, error) { + // Return cached hashes if they have ALL already been generated. This + // flag is necessary because the transaction hashes are lazily generated + // in a sparse fashion. + if b.txShasGenerated { + return b.txShas, nil + } + + // Generate slice to hold all of the transaction hashes if needed. + if len(b.txShas) == 0 { + b.txShas = make([]*btcwire.ShaHash, b.msgBlock.Header.TxnCount) + } + + // Generate and cache the transaction hashes for all that haven't already + // been done. + for i, hash := range b.txShas { + if hash == nil { + // Ignore the error since TxSha can't currently fail. + sha, _ := b.msgBlock.Transactions[i].TxSha(b.protocolVersion) + b.txShas[i] = &sha + } + } + + b.txShasGenerated = true + return b.txShas, nil +} + +// ProtocolVersion returns the protocol version that was used to create the +// underlying btcwire.MsgBlock. +func (b *Block) ProtocolVersion() uint32 { + return b.protocolVersion +} + +// TxLoc() returns the offsets and lengths of each transaction in a raw block. +// It is used to allow fast indexing into the +func (b *Block) TxLoc() (txlocD []btcwire.TxLoc, err error) { + rawMsg, pver, err := b.Bytes() + if err != nil { + return + } + rbuf := bytes.NewBuffer(rawMsg) + + var mblock btcwire.MsgBlock + txloc, err := mblock.BtcDecodeTxLoc(rbuf, pver) + if err != nil { + return + } + return txloc, err +} + +// Height returns the saved height of the block in the blockchain. This value +// will be BlockHeightUnknown if it hasn't already explicitly been set. +func (b *Block) Height() int64 { + return b.blockHeight +} + +// SetHeight sets the height of the block in the blockchain. +func (b *Block) SetHeight(height int64) { + b.blockHeight = height +} + +// NewBlock returns a new instance of a bitcoin block given an underlying +// btcwire.MsgBlock and protocol version. See Block. +func NewBlock(msgBlock *btcwire.MsgBlock, pver uint32) *Block { + return &Block{ + msgBlock: msgBlock, + protocolVersion: pver, + blockHeight: BlockHeightUnknown, + } +} + +// NewBlockFromBytes returns a new instance of a bitcoin block given the +// raw wire encoded bytes and protocol version used to encode those bytes. +// See Block. +func NewBlockFromBytes(rawBlock []byte, pver uint32) (*Block, error) { + // Decode the raw block bytes into a MsgBlock. + var msgBlock btcwire.MsgBlock + br := bytes.NewBuffer(rawBlock) + err := msgBlock.BtcDecode(br, pver) + if err != nil { + return nil, err + } + + b := Block{ + msgBlock: &msgBlock, + rawBlock: rawBlock, + protocolVersion: pver, + blockHeight: BlockHeightUnknown, + } + return &b, nil +} + +// NewBlockFromBlockAndBytes returns a new instance of a bitcoin block given +// an underlying btcwire.MsgBlock, protocol version and raw Block. See Block. +func NewBlockFromBlockAndBytes(msgBlock *btcwire.MsgBlock, rawBlock []byte, pver uint32) *Block { + return &Block{ + msgBlock: msgBlock, + rawBlock: rawBlock, + protocolVersion: pver, + blockHeight: BlockHeightUnknown, + } +} diff --git a/block_test.go b/block_test.go new file mode 100644 index 0000000..52daa21 --- /dev/null +++ b/block_test.go @@ -0,0 +1,487 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcutil_test + +import ( + "bytes" + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" + "github.com/davecgh/go-spew/spew" + "reflect" + "testing" + "time" +) + +// TestBlock tests the API for Block. +func TestBlock(t *testing.T) { + pver := btcwire.ProtocolVersion + b := btcutil.NewBlock(&Block100000, pver) + + // Ensure we get the same data back out. + if gotPver := b.ProtocolVersion(); gotPver != pver { + t.Errorf("ProtocolVersion: wrong protocol version - got %v, want %v", + gotPver, pver) + } + if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { + t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", + spew.Sdump(msgBlock), spew.Sdump(&Block100000)) + } + + // Ensure block height set and get work properly. + wantHeight := int64(100000) + b.SetHeight(wantHeight) + if gotHeight := b.Height(); gotHeight != wantHeight { + t.Errorf("Height: mismatched height - got %v, want %v", + gotHeight, wantHeight) + } + + // Hash for block 100,000. + wantShaStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" + wantSha, err := btcwire.NewShaHashFromStr(wantShaStr) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Request the sha multiple times to test generation and caching. + for i := 0; i < 2; i++ { + sha, err := b.Sha() + if err != nil { + t.Errorf("Sha: %v", err) + continue + } + if !sha.IsEqual(wantSha) { + t.Errorf("Sha #%d mismatched sha - got %v, want %v", i, + sha, wantSha) + } + } + + // Shas for the transactions in Block100000. + wantTxShas := []string{ + "8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87", + "fff2525b8931402dd09222c50775608f75787bd2b87e56995a7bdd30f79702c4", + "6359f0868171b1d194cbee1af2f16ea598ae8fad666d9b012c8ed2b79a236ec4", + "e9a66845e05d5abc0ad04ec80f774a7e585c6e8db975962d069a522137b80c1d", + } + + // Request sha for all transactions one at a time. + for i, txSha := range wantTxShas { + wantSha, err := btcwire.NewShaHashFromStr(txSha) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + // Request the sha multiple times to test generation and caching. + for j := 0; j < 2; j++ { + sha, err := b.TxSha(i) + if err != nil { + t.Errorf("TxSha: %v", err) + continue + } + if !sha.IsEqual(wantSha) { + t.Errorf("TxSha #%d mismatched sha - got %v, "+ + "want %v", j, sha, wantSha) + continue + } + } + } + + // Create a new block to nuke all cached data. + b = btcutil.NewBlock(&Block100000, pver) + + // Request slice of all transaction shas multiple times to test + // generation and caching. + for i := 0; i < 2; i++ { + txShas, err := b.TxShas() + if err != nil { + t.Errorf("TxShas: %v", err) + continue + } + + // Ensure we get the expected number of transaction shas. + if len(txShas) != len(wantTxShas) { + t.Errorf("TxShas #%d mismatched number of shas -"+ + "got %d, want %d", i, len(txShas), + len(wantTxShas)) + continue + } + + // Ensure all of the shas match. + for j, txSha := range wantTxShas { + wantSha, err := btcwire.NewShaHashFromStr(txSha) + if err != nil { + t.Errorf("NewShaHashFromStr: %v", err) + } + + if !txShas[j].IsEqual(wantSha) { + t.Errorf("TxShas #%d mismatched shas - "+ + "got %v, want %v", j, + spew.Sdump(txShas), + spew.Sdump(wantTxShas)) + continue + } + } + } + + // Encode the test block to bytes. + var block100000Buf bytes.Buffer + err = Block100000.BtcEncode(&block100000Buf, pver) + if err != nil { + t.Errorf("BtcEncode: %v", err) + } + block100000Bytes := block100000Buf.Bytes() + + // Request raw bytes multiple times to test generation and caching. + for i := 0; i < 2; i++ { + rawBytes, tmpPver, err := b.Bytes() + if err != nil { + t.Errorf("Bytes: %v", err) + continue + } + if !bytes.Equal(rawBytes, block100000Bytes) { + t.Errorf("Bytes #%d wrong bytes - got %v, want %v", i, + spew.Sdump(rawBytes), + spew.Sdump(block100000Bytes)) + continue + } + if tmpPver != pver { + t.Errorf("Bytes #%d wrong protocol version - "+ + "got %v, want %v", i, spew.Sdump(rawBytes), + spew.Sdump(block100000Bytes)) + continue + + } + } +} + +// TestNewBlockFromBytes tests creation of a Block from raw bytes. +func TestNewBlockFromBytes(t *testing.T) { + // Encode the test block to bytes. + pver := btcwire.ProtocolVersion + var block100000Buf bytes.Buffer + err := Block100000.BtcEncode(&block100000Buf, pver) + if err != nil { + t.Errorf("BtcEncode: %v", err) + } + block100000Bytes := block100000Buf.Bytes() + + // Create a new block from the encoded bytes. + b, err := btcutil.NewBlockFromBytes(block100000Bytes, pver) + if err != nil { + t.Errorf("NewBlockFromBytes: %v", err) + return + } + + // Ensure we get the same data back out. + rawBytes, tmpPver, err := b.Bytes() + if err != nil { + t.Errorf("Bytes: %v", err) + return + } + if !bytes.Equal(rawBytes, block100000Bytes) { + t.Errorf("Bytes: wrong bytes - got %v, want %v", + spew.Sdump(rawBytes), + spew.Sdump(block100000Bytes)) + } + if tmpPver != pver { + t.Errorf("Bytes: wrong protocol version - got %v, want %v", + tmpPver, pver) + } + + // Ensure the generated MsgBlock is correct. + if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { + t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", + spew.Sdump(msgBlock), spew.Sdump(&Block100000)) + } +} + +// TestNewBlockFromBlockAndBytes tests creation of a Block from a MsgBlock and +// raw bytes. +func TestNewBlockFromBlockAndBytes(t *testing.T) { + // Encode the test block to bytes. + pver := btcwire.ProtocolVersion + var block100000Buf bytes.Buffer + err := Block100000.BtcEncode(&block100000Buf, pver) + if err != nil { + t.Errorf("BtcEncode: %v", err) + } + block100000Bytes := block100000Buf.Bytes() + + // Create a new block from the encoded bytes. + b := btcutil.NewBlockFromBlockAndBytes(&Block100000, + block100000Bytes, pver) + + // Ensure we get the same data back out. + rawBytes, tmpPver, err := b.Bytes() + if err != nil { + t.Errorf("Bytes: %v", err) + return + } + if !bytes.Equal(rawBytes, block100000Bytes) { + t.Errorf("Bytes: wrong bytes - got %v, want %v", + spew.Sdump(rawBytes), + spew.Sdump(block100000Bytes)) + } + if tmpPver != pver { + t.Errorf("Bytes: wrong protocol version - got %v, want %v", + tmpPver, pver) + } + if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) { + t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v", + spew.Sdump(msgBlock), spew.Sdump(&Block100000)) + } +} + +// Block100000 defines block 100,000 of the block chain. It is used to +// test Block operations. +var Block100000 btcwire.MsgBlock = btcwire.MsgBlock{ + Header: btcwire.BlockHeader{ + Version: 1, + PrevBlock: btcwire.ShaHash([32]byte{ // Make go vet happy. + 0x50, 0x12, 0x01, 0x19, 0x17, 0x2a, 0x61, 0x04, + 0x21, 0xa6, 0xc3, 0x01, 0x1d, 0xd3, 0x30, 0xd9, + 0xdf, 0x07, 0xb6, 0x36, 0x16, 0xc2, 0xcc, 0x1f, + 0x1c, 0xd0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + }), // 000000000002d01c1fccc21636b607dfd930d31d01c3a62104612a1719011250 + MerkleRoot: btcwire.ShaHash([32]byte{ // Make go vet happy. + 0x66, 0x57, 0xa9, 0x25, 0x2a, 0xac, 0xd5, 0xc0, + 0xb2, 0x94, 0x09, 0x96, 0xec, 0xff, 0x95, 0x22, + 0x28, 0xc3, 0x06, 0x7c, 0xc3, 0x8d, 0x48, 0x85, + 0xef, 0xb5, 0xa4, 0xac, 0x42, 0x47, 0xe9, 0xf3, + }), // f3e94742aca4b5ef85488dc37c06c3282295ffec960994b2c0d5ac2a25a95766 + Timestamp: time.Unix(1293623863, 0), // 2010-12-29 11:57:43 +0000 UTC + Bits: 0x1b04864c, // 453281356 + Nonce: 0x10572b0f, // 274148111 + TxnCount: 4, + }, + Transactions: []*btcwire.MsgTx{ + &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{ + 0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02, + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0x12a05f200, // 5000000000 + PkScript: []byte{ + 0x41, // OP_DATA_65 + 0x04, 0x1b, 0x0e, 0x8c, 0x25, 0x67, 0xc1, 0x25, + 0x36, 0xaa, 0x13, 0x35, 0x7b, 0x79, 0xa0, 0x73, + 0xdc, 0x44, 0x44, 0xac, 0xb8, 0x3c, 0x4e, 0xc7, + 0xa0, 0xe2, 0xf9, 0x9d, 0xd7, 0x45, 0x75, 0x16, + 0xc5, 0x81, 0x72, 0x42, 0xda, 0x79, 0x69, 0x24, + 0xca, 0x4e, 0x99, 0x94, 0x7d, 0x08, 0x7f, 0xed, + 0xf9, 0xce, 0x46, 0x7c, 0xb9, 0xf7, 0xc6, 0x28, + 0x70, 0x78, 0xf8, 0x01, 0xdf, 0x27, 0x6f, 0xdf, + 0x84, // 65-byte signature + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ // Make go vet happy. + 0x03, 0x2e, 0x38, 0xe9, 0xc0, 0xa8, 0x4c, 0x60, + 0x46, 0xd6, 0x87, 0xd1, 0x05, 0x56, 0xdc, 0xac, + 0xc4, 0x1d, 0x27, 0x5e, 0xc5, 0x5f, 0xc0, 0x07, + 0x79, 0xac, 0x88, 0xfd, 0xf3, 0x57, 0xa1, 0x87, + }), // 87a157f3fd88ac7907c05fc55e271dc4acdc5605d187d646604ca8c0e9382e03 + Index: 0, + }, + SignatureScript: []byte{ + 0x49, // OP_DATA_73 + 0x30, 0x46, 0x02, 0x21, 0x00, 0xc3, 0x52, 0xd3, + 0xdd, 0x99, 0x3a, 0x98, 0x1b, 0xeb, 0xa4, 0xa6, + 0x3a, 0xd1, 0x5c, 0x20, 0x92, 0x75, 0xca, 0x94, + 0x70, 0xab, 0xfc, 0xd5, 0x7d, 0xa9, 0x3b, 0x58, + 0xe4, 0xeb, 0x5d, 0xce, 0x82, 0x02, 0x21, 0x00, + 0x84, 0x07, 0x92, 0xbc, 0x1f, 0x45, 0x60, 0x62, + 0x81, 0x9f, 0x15, 0xd3, 0x3e, 0xe7, 0x05, 0x5c, + 0xf7, 0xb5, 0xee, 0x1a, 0xf1, 0xeb, 0xcc, 0x60, + 0x28, 0xd9, 0xcd, 0xb1, 0xc3, 0xaf, 0x77, 0x48, + 0x01, // 73-byte signature + 0x41, // OP_DATA_65 + 0x04, 0xf4, 0x6d, 0xb5, 0xe9, 0xd6, 0x1a, 0x9d, + 0xc2, 0x7b, 0x8d, 0x64, 0xad, 0x23, 0xe7, 0x38, + 0x3a, 0x4e, 0x6c, 0xa1, 0x64, 0x59, 0x3c, 0x25, + 0x27, 0xc0, 0x38, 0xc0, 0x85, 0x7e, 0xb6, 0x7e, + 0xe8, 0xe8, 0x25, 0xdc, 0xa6, 0x50, 0x46, 0xb8, + 0x2c, 0x93, 0x31, 0x58, 0x6c, 0x82, 0xe0, 0xfd, + 0x1f, 0x63, 0x3f, 0x25, 0xf8, 0x7c, 0x16, 0x1b, + 0xc6, 0xf8, 0xa6, 0x30, 0x12, 0x1d, 0xf2, 0xb3, + 0xd3, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0x2123e300, // 556000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0xc3, 0x98, 0xef, 0xa9, 0xc3, 0x92, 0xba, 0x60, + 0x13, 0xc5, 0xe0, 0x4e, 0xe7, 0x29, 0x75, 0x5e, + 0xf7, 0xf5, 0x8b, 0x32, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + &btcwire.TxOut{ + Value: 0x108e20f00, // 4444000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x94, 0x8c, 0x76, 0x5a, 0x69, 0x14, 0xd4, 0x3f, + 0x2a, 0x7a, 0xc1, 0x77, 0xda, 0x2c, 0x2f, 0x6b, + 0x52, 0xde, 0x3d, 0x7c, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ // Make go vet happy. + 0xc3, 0x3e, 0xbf, 0xf2, 0xa7, 0x09, 0xf1, 0x3d, + 0x9f, 0x9a, 0x75, 0x69, 0xab, 0x16, 0xa3, 0x27, + 0x86, 0xaf, 0x7d, 0x7e, 0x2d, 0xe0, 0x92, 0x65, + 0xe4, 0x1c, 0x61, 0xd0, 0x78, 0x29, 0x4e, 0xcf, + }), // cf4e2978d0611ce46592e02d7e7daf8627a316ab69759a9f3df109a7f2bf3ec3 + Index: 1, + }, + SignatureScript: []byte{ + 0x47, // OP_DATA_71 + 0x30, 0x44, 0x02, 0x20, 0x03, 0x2d, 0x30, 0xdf, + 0x5e, 0xe6, 0xf5, 0x7f, 0xa4, 0x6c, 0xdd, 0xb5, + 0xeb, 0x8d, 0x0d, 0x9f, 0xe8, 0xde, 0x6b, 0x34, + 0x2d, 0x27, 0x94, 0x2a, 0xe9, 0x0a, 0x32, 0x31, + 0xe0, 0xba, 0x33, 0x3e, 0x02, 0x20, 0x3d, 0xee, + 0xe8, 0x06, 0x0f, 0xdc, 0x70, 0x23, 0x0a, 0x7f, + 0x5b, 0x4a, 0xd7, 0xd7, 0xbc, 0x3e, 0x62, 0x8c, + 0xbe, 0x21, 0x9a, 0x88, 0x6b, 0x84, 0x26, 0x9e, + 0xae, 0xb8, 0x1e, 0x26, 0xb4, 0xfe, 0x01, + 0x41, // OP_DATA_65 + 0x04, 0xae, 0x31, 0xc3, 0x1b, 0xf9, 0x12, 0x78, + 0xd9, 0x9b, 0x83, 0x77, 0xa3, 0x5b, 0xbc, 0xe5, + 0xb2, 0x7d, 0x9f, 0xff, 0x15, 0x45, 0x68, 0x39, + 0xe9, 0x19, 0x45, 0x3f, 0xc7, 0xb3, 0xf7, 0x21, + 0xf0, 0xba, 0x40, 0x3f, 0xf9, 0x6c, 0x9d, 0xee, + 0xb6, 0x80, 0xe5, 0xfd, 0x34, 0x1c, 0x0f, 0xc3, + 0xa7, 0xb9, 0x0d, 0xa4, 0x63, 0x1e, 0xe3, 0x95, + 0x60, 0x63, 0x9d, 0xb4, 0x62, 0xe9, 0xcb, 0x85, + 0x0f, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0xf4240, // 1000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0xb0, 0xdc, 0xbf, 0x97, 0xea, 0xbf, 0x44, 0x04, + 0xe3, 0x1d, 0x95, 0x24, 0x77, 0xce, 0x82, 0x2d, + 0xad, 0xbe, 0x7e, 0x10, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + &btcwire.TxOut{ + Value: 0x11d260c0, // 299000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x6b, 0x12, 0x81, 0xee, 0xc2, 0x5a, 0xb4, 0xe1, + 0xe0, 0x79, 0x3f, 0xf4, 0xe0, 0x8a, 0xb1, 0xab, + 0xb3, 0x40, 0x9c, 0xd9, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ // Make go vet happy. + 0x0b, 0x60, 0x72, 0xb3, 0x86, 0xd4, 0xa7, 0x73, + 0x23, 0x52, 0x37, 0xf6, 0x4c, 0x11, 0x26, 0xac, + 0x3b, 0x24, 0x0c, 0x84, 0xb9, 0x17, 0xa3, 0x90, + 0x9b, 0xa1, 0xc4, 0x3d, 0xed, 0x5f, 0x51, 0xf4, + }), // f4515fed3dc4a19b90a317b9840c243bac26114cf637522373a7d486b372600b + Index: 0, + }, + SignatureScript: []byte{ + 0x49, // OP_DATA_73 + 0x30, 0x46, 0x02, 0x21, 0x00, 0xbb, 0x1a, 0xd2, + 0x6d, 0xf9, 0x30, 0xa5, 0x1c, 0xce, 0x11, 0x0c, + 0xf4, 0x4f, 0x7a, 0x48, 0xc3, 0xc5, 0x61, 0xfd, + 0x97, 0x75, 0x00, 0xb1, 0xae, 0x5d, 0x6b, 0x6f, + 0xd1, 0x3d, 0x0b, 0x3f, 0x4a, 0x02, 0x21, 0x00, + 0xc5, 0xb4, 0x29, 0x51, 0xac, 0xed, 0xff, 0x14, + 0xab, 0xba, 0x27, 0x36, 0xfd, 0x57, 0x4b, 0xdb, + 0x46, 0x5f, 0x3e, 0x6f, 0x8d, 0xa1, 0x2e, 0x2c, + 0x53, 0x03, 0x95, 0x4a, 0xca, 0x7f, 0x78, 0xf3, + 0x01, // 73-byte signature + 0x41, // OP_DATA_65 + 0x04, 0xa7, 0x13, 0x5b, 0xfe, 0x82, 0x4c, 0x97, + 0xec, 0xc0, 0x1e, 0xc7, 0xd7, 0xe3, 0x36, 0x18, + 0x5c, 0x81, 0xe2, 0xaa, 0x2c, 0x41, 0xab, 0x17, + 0x54, 0x07, 0xc0, 0x94, 0x84, 0xce, 0x96, 0x94, + 0xb4, 0x49, 0x53, 0xfc, 0xb7, 0x51, 0x20, 0x65, + 0x64, 0xa9, 0xc2, 0x4d, 0xd0, 0x94, 0xd4, 0x2f, + 0xdb, 0xfd, 0xd5, 0xaa, 0xd3, 0xe0, 0x63, 0xce, + 0x6a, 0xf4, 0xcf, 0xaa, 0xea, 0x4e, 0xa1, 0x4f, + 0xbb, // 65-byte pubkey + }, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0xf4240, // 1000000 + PkScript: []byte{ + 0x76, // OP_DUP + 0xa9, // OP_HASH160 + 0x14, // OP_DATA_20 + 0x39, 0xaa, 0x3d, 0x56, 0x9e, 0x06, 0xa1, 0xd7, + 0x92, 0x6d, 0xc4, 0xbe, 0x11, 0x93, 0xc9, 0x9b, + 0xf2, 0xeb, 0x9e, 0xe0, + 0x88, // OP_EQUALVERIFY + 0xac, // OP_CHECKSIG + }, + }, + }, + LockTime: 0, + }, + }, +} diff --git a/cov_report.sh b/cov_report.sh new file mode 100644 index 0000000..307f05b --- /dev/null +++ b/cov_report.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +type gocov >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi +gocov test | gocov report diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..0902935 --- /dev/null +++ b/doc.go @@ -0,0 +1,15 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package btcutil provides bitcoin-specific convenience functions and types. + +Block Overview + +A Block defines a bitcoin block that provides easier and more efficient +manipulation of raw wire protocol blocks. It also memoizes hashes for the +block and its transactions on their first access so subsequent accesses don't +have to repeat the relatively expensive hashing operations. +*/ +package btcutil diff --git a/test_coverage.txt b/test_coverage.txt new file mode 100644 index 0000000..dad4595 --- /dev/null +++ b/test_coverage.txt @@ -0,0 +1,16 @@ + +github.com/conformal/btcutil/block.go Block.TxShas 100.00% (10/10) +github.com/conformal/btcutil/block.go Block.Sha 100.00% (5/5) +github.com/conformal/btcutil/block.go NewBlockFromBlockAndBytes 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.MsgBlock 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.ProtocolVersion 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.Height 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.SetHeight 100.00% (1/1) +github.com/conformal/btcutil/block.go NewBlock 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.Bytes 88.89% (8/9) +github.com/conformal/btcutil/block.go NewBlockFromBytes 85.71% (6/7) +github.com/conformal/btcutil/block.go Block.TxSha 81.82% (9/11) +github.com/conformal/btcutil/block.go Block.TxLoc 0.00% (0/9) +github.com/conformal/btcutil/block.go OutOfRangeError.Error 0.00% (0/1) +github.com/conformal/btcutil ------------------------- 75.86% (44/58) +