Skip to content

Commit

Permalink
Initial implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
davecgh committed May 28, 2013
1 parent 727d687 commit ef6c019
Show file tree
Hide file tree
Showing 7 changed files with 825 additions and 0 deletions.
13 changes: 13 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
226 changes: 226 additions & 0 deletions block.go
Original file line number Diff line number Diff line change
@@ -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,
}
}
Loading

0 comments on commit ef6c019

Please sign in to comment.