-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
blockchain/standalone: Implement a new module.
This implements a new module named blockchain/standalone which aims to provide several of the standalone functions currently available in blockchain and ultimately replace them in the next major version. The primary goal of offering these functions via a separate module is to reduce the required dependencies to a minimum as compared to the blockchain module. It will be ideal for applications such as lightweight clients that need to ensure basic security properties hold and calculate appropriate vote subsidies and block explorers. For example, some things an SPV wallet needs to prove are that the block headers all connect together, that they satisfy the proof of work requirements, and that a given transaction tree is valid for a given header. The new module currently only provides functions related to proof of work, however, future commits will provide functions for merkle root calculation, subsidy calculation, and coinbase transaction identification. It is being done in stages to help ease review since it is consensus critical code. Finally, it also includes comprehensive tests, full package documentation, and basic usage examples.
- Loading branch information
Showing
9 changed files
with
918 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
standalone | ||
========== | ||
|
||
[![Build Status](https://img.shields.io/travis/decred/dcrd.svg)](https://travis-ci.org/decred/dcrd) | ||
[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) | ||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/decred/dcrd/blockchain/standalone) | ||
|
||
Package standalone provides standalone functions useful for working with the | ||
Decred blockchain consensus rules. | ||
|
||
The primary goal of offering these functions via a separate module is to reduce | ||
the required dependencies to a minimum as compared to the blockchain module. | ||
|
||
It is ideal for applications such as lightweight clients that need to ensure | ||
basic security properties hold and calculate appropriate vote subsidies and | ||
block explorers. | ||
|
||
For example, some things an SPV wallet needs to prove are that the block headers | ||
all connect together, that they satisfy the proof of work requirements, and that | ||
a given transaction tree is valid for a given header. | ||
|
||
The provided functions fall into the following categories: | ||
|
||
- Proof-of-work | ||
- Converting to and from the compact target difficulty representation | ||
- Calculating work values based on the compact target difficulty | ||
- Checking a block hash satisfies a target difficulty and that target | ||
difficulty is within a valid range | ||
- Merkle root calculation (WIP - not yet available) | ||
- Subsidy calculation (WIP - not yet available) | ||
- Coinbase transaction identification (WIP - not yet available) | ||
|
||
## Installation and Updating | ||
|
||
```bash | ||
$ go get -u github.com/decred/dcrd/blockchain/standalone | ||
``` | ||
|
||
## Examples | ||
|
||
* [CompactToBig Example](https://godoc.org/github.com/decred/dcrd/blockchain/standalone#example-CompactToBig) | ||
Demonstrates how to convert the compact "bits" in a block header which | ||
represent the target difficulty to a big integer and display it using the | ||
typical hex notation. | ||
|
||
* [BigToCompact Example](https://godoc.org/github.com/decred/dcrd/blockchain/standalone#example-BigToCompact) | ||
Demonstrates how to convert a target difficulty into the compact "bits" in a | ||
block header which represent that target difficulty. | ||
|
||
* [CheckProofOfWork Example](https://godoc.org/github.com/decred/dcrd/blockchain/standalone#example-CheckProofOfWork) | ||
Demonstrates checking the proof of work of a block hash against | ||
a target difficulty. | ||
|
||
## License | ||
|
||
Package standalone is licensed under the [copyfree](http://copyfree.org) ISC | ||
License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) 2019 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
/* | ||
Package standalone provides standalone functions useful for working with the | ||
Decred blockchain consensus rules. | ||
The primary goal of offering these functions via a separate module is to reduce | ||
the required dependencies to a minimum as compared to the blockchain module. | ||
It is ideal for applications such as lightweight clients that need to ensure | ||
basic security properties hold and calculate appropriate vote subsidies and | ||
block explorers. | ||
For example, some things an SPV wallet needs to prove are that the block headers | ||
all connect together, that they satisfy the proof of work requirements, and that | ||
a given transaction tree is valid for a given header. | ||
The provided functions fall into the following categories: | ||
- Proof-of-work | ||
- Converting to and from the compact target difficulty representation | ||
- Calculating work values based on the compact target difficulty | ||
- Checking a block hash satisfies a target difficulty and that target | ||
difficulty is within a valid range | ||
- Merkle root calculation (WIP - not yet available) | ||
- Subsidy calculation (WIP - not yet available) | ||
- Coinbase transaction identification (WIP - not yet available) | ||
Errors | ||
Errors returned by this package are of type standalone.RuleError. This allows | ||
the caller to differentiate between errors further up the call stack through | ||
type assertions. In addition, callers can programmatically determine the | ||
specific rule violation by examining the ErrorCode field of the type asserted | ||
standalone.RuleError. | ||
*/ | ||
package standalone |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright (c) 2019 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package standalone | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
// ErrorCode identifies a kind of error. | ||
type ErrorCode int | ||
|
||
// These constants are used to identify a specific RuleError. | ||
const ( | ||
// ErrUnexpectedDifficulty indicates specified bits do not align with | ||
// the expected value either because it doesn't match the calculated | ||
// value based on difficulty rules or it is out of the valid range. | ||
ErrUnexpectedDifficulty ErrorCode = iota | ||
|
||
// ErrHighHash indicates the block does not hash to a value which is | ||
// lower than the required target difficultly. | ||
ErrHighHash | ||
|
||
// numErrorCodes is the maximum error code number used in tests. | ||
numErrorCodes | ||
) | ||
|
||
// Map of ErrorCode values back to their constant names for pretty printing. | ||
var errorCodeStrings = map[ErrorCode]string{ | ||
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty", | ||
ErrHighHash: "ErrHighHash", | ||
} | ||
|
||
// String returns the ErrorCode as a human-readable name. | ||
func (e ErrorCode) String() string { | ||
if s := errorCodeStrings[e]; s != "" { | ||
return s | ||
} | ||
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e)) | ||
} | ||
|
||
// RuleError identifies a rule violation. The caller can use type assertions to | ||
// determine if a failure was specifically due to a rule violation and access | ||
// the ErrorCode field to ascertain the specific reason for the rule violation. | ||
type RuleError struct { | ||
ErrorCode ErrorCode // Describes the kind of error | ||
Description string // Human readable description of the issue | ||
} | ||
|
||
// Error satisfies the error interface and prints human-readable errors. | ||
func (e RuleError) Error() string { | ||
return e.Description | ||
} | ||
|
||
// ruleError creates an RuleError given a set of arguments. | ||
func ruleError(c ErrorCode, desc string) RuleError { | ||
return RuleError{ErrorCode: c, Description: desc} | ||
} | ||
|
||
// IsErrorCode returns whether or not the provided error is a rule error with | ||
// the provided error code. | ||
func IsErrorCode(err error, c ErrorCode) bool { | ||
e, ok := err.(RuleError) | ||
return ok && e.ErrorCode == c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// Copyright (c) 2019 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package standalone | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
// TestErrorCodeStringer tests the stringized output for the ErrorCode type. | ||
func TestErrorCodeStringer(t *testing.T) { | ||
tests := []struct { | ||
in ErrorCode | ||
want string | ||
}{ | ||
{ErrUnexpectedDifficulty, "ErrUnexpectedDifficulty"}, | ||
{ErrHighHash, "ErrHighHash"}, | ||
{0xffff, "Unknown ErrorCode (65535)"}, | ||
} | ||
|
||
// Detect additional error codes that don't have the stringer added. | ||
if len(tests)-1 != int(numErrorCodes) { | ||
t.Errorf("It appears an error code was added without adding an " + | ||
"associated stringer test") | ||
} | ||
|
||
t.Logf("Running %d tests", len(tests)) | ||
for i, test := range tests { | ||
result := test.in.String() | ||
if result != test.want { | ||
t.Errorf("String #%d\n got: %s want: %s", i, result, test.want) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// TestRuleError tests the error output for the RuleError type. | ||
func TestRuleError(t *testing.T) { | ||
tests := []struct { | ||
in RuleError | ||
want string | ||
}{{ | ||
RuleError{Description: "duplicate block"}, | ||
"duplicate block", | ||
}, { | ||
RuleError{Description: "human-readable error"}, | ||
"human-readable error", | ||
}, | ||
} | ||
|
||
t.Logf("Running %d tests", len(tests)) | ||
for i, test := range tests { | ||
result := test.in.Error() | ||
if result != test.want { | ||
t.Errorf("Error #%d\n got: %s want: %s", i, result, test.want) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
// TestIsErrorCode ensures IsErrorCode works as intended. | ||
func TestIsErrorCode(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
err error | ||
code ErrorCode | ||
want bool | ||
}{{ | ||
name: "ErrUnexpectedDifficulty testing for ErrUnexpectedDifficulty", | ||
err: ruleError(ErrUnexpectedDifficulty, ""), | ||
code: ErrUnexpectedDifficulty, | ||
want: true, | ||
}, { | ||
name: "ErrHighHash testing for ErrHighHash", | ||
err: ruleError(ErrHighHash, ""), | ||
code: ErrHighHash, | ||
want: true, | ||
}, { | ||
name: "ErrHighHash error testing for ErrUnexpectedDifficulty", | ||
err: ruleError(ErrHighHash, ""), | ||
code: ErrUnexpectedDifficulty, | ||
want: false, | ||
}, { | ||
name: "ErrHighHash error testing for unknown error code", | ||
err: ruleError(ErrHighHash, ""), | ||
code: 0xffff, | ||
want: false, | ||
}, { | ||
name: "nil error testing for ErrUnexpectedDifficulty", | ||
err: nil, | ||
code: ErrUnexpectedDifficulty, | ||
want: false, | ||
}} | ||
for _, test := range tests { | ||
result := IsErrorCode(test.err, test.code) | ||
if result != test.want { | ||
t.Errorf("%s: unexpected result -- got: %v want: %v", test.name, | ||
result, test.want) | ||
continue | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Copyright (c) 2014-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 standalone_test | ||
|
||
import ( | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/decred/dcrd/blockchain/standalone" | ||
"github.com/decred/dcrd/chaincfg/chainhash" | ||
) | ||
|
||
// This example demonstrates how to convert the compact "bits" in a block header | ||
// which represent the target difficulty to a big integer and display it using | ||
// the typical hex notation. | ||
func ExampleCompactToBig() { | ||
// Convert the bits from block 1 in the main chain. | ||
bits := uint32(453115903) | ||
targetDifficulty := standalone.CompactToBig(bits) | ||
|
||
// Display it in hex. | ||
fmt.Printf("%064x\n", targetDifficulty.Bytes()) | ||
|
||
// Output: | ||
// 000000000001ffff000000000000000000000000000000000000000000000000 | ||
} | ||
|
||
// This example demonstrates how to convert a target difficulty into the compact | ||
// "bits" in a block header which represent that target difficulty. | ||
func ExampleBigToCompact() { | ||
// Convert the target difficulty from block 1 in the main chain to compact | ||
// form. | ||
t := "000000000001ffff000000000000000000000000000000000000000000000000" | ||
targetDifficulty, success := new(big.Int).SetString(t, 16) | ||
if !success { | ||
fmt.Println("invalid target difficulty") | ||
return | ||
} | ||
bits := standalone.BigToCompact(targetDifficulty) | ||
|
||
fmt.Println(bits) | ||
|
||
// Output: | ||
// 453115903 | ||
} | ||
|
||
// This example demonstrates checking the proof of work of a block hash against | ||
// a target difficulty. | ||
func ExampleCheckProofOfWork() { | ||
// This is the pow limit for mainnet and would ordinarily come from chaincfg | ||
// params, however, it is hard coded here for the purposes of the example. | ||
l := "00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" | ||
powLimit, success := new(big.Int).SetString(l, 16) | ||
if !success { | ||
fmt.Println("invalid pow limit") | ||
return | ||
} | ||
|
||
// Check the proof of work for block 1 in the main chain. | ||
h := "000000000000437482b6d47f82f374cde539440ddb108b0a76886f0d87d126b9" | ||
hash, err := chainhash.NewHashFromStr(h) | ||
if err != nil { | ||
fmt.Printf("failed to parse hash: %v\n", err) | ||
return | ||
} | ||
bits := uint32(453115903) | ||
|
||
if err := standalone.CheckProofOfWork(hash, bits, powLimit); err != nil { | ||
fmt.Printf("proof of work check failed: %v\n", err) | ||
return | ||
} | ||
|
||
// Output: | ||
// | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/decred/dcrd/blockchain/standalone | ||
|
||
go 1.11 | ||
|
||
require github.com/decred/dcrd/chaincfg/chainhash v1.0.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/dchest/blake256 v1.0.0 h1:6gUgI5MHdz9g0TdrgKqXsoDX+Zjxmm1Sc6OsoGru50I= | ||
github.com/dchest/blake256 v1.0.0/go.mod h1:xXNWCE1jsAP8DAjP+rKw2MbeqLczjI3TRx2VK+9OEYY= | ||
github.com/decred/dcrd/chaincfg/chainhash v1.0.1 h1:0vG7U9+dSjSCaHQKdoSKURK2pOb47+b+8FK5q4+Je7M= | ||
github.com/decred/dcrd/chaincfg/chainhash v1.0.1/go.mod h1:OVfvaOsNLS/A1y4Eod0Ip/Lf8qga7VXCQjUQLbkY0Go= |
Oops, something went wrong.