dcrd/blockchain/standalone/example_test.go
Dave Collins bd4e5c21dc
blockchain/standalone: Add merkle root calc funcs.
This adds functions to calculate a merkle root to the
blockchain/standalone module.  Rather than copying the existing
functions from blockchain, it introduces new optimized functions that
have simpler code and are also generally easier to use.  The new
functions simply return the calculated root instead of all of the
intermediate hashes which no known callers were actually using anyway.

It also adds a basic usage example, benchmarks, updates the
documentation and includes comprehensive tests.

As can be seen by the benchmarks below, the new code isn't significantly
faster in terms of execution speed since it is still dominated by the
actual hashing, however, it does significantly reduce the number of
allocations which reduces pressure on the GC, particularly during bursty
situations such as IBD.  It also scales much better to larger numbers of
transactions.

The following is a before and after comparison of calculating merkle
roots for various numbers of leaves:

benchmark                       old ns/op    new ns/op   delta
---------------------------------------------------------------
BenchmarkCalcMerkleRoot/20      25637        24287       -5.27%
BenchmarkCalcMerkleRoot/1000    1252749      1209130     -3.48%
BenchmarkCalcMerkleRoot/2000    2601754      2343198     -9.94%
BenchmarkCalcMerkleRoot/4000    4873969      4629169     -5.02%
BenchmarkCalcMerkleRoot/8000    9831116      9270095     -5.71%
BenchmarkCalcMerkleRoot/16000   19172545     19067585    -0.55%
BenchmarkCalcMerkleRoot/32000   37980323     35746500    -5.88%

benchmark                       old allocs   new allocs  delta
----------------------------------------------------------------
BenchmarkCalcMerkleRoot/20      106          64          -39.62%
BenchmarkCalcMerkleRoot/1000    5006         3004        -39.99%
BenchmarkCalcMerkleRoot/2000    10006        6004        -40.00%
BenchmarkCalcMerkleRoot/4000    20006        12004       -40.00%
BenchmarkCalcMerkleRoot/8000    40006        24004       -40.00%
BenchmarkCalcMerkleRoot/16000   80006        48004       -40.00%
BenchmarkCalcMerkleRoot/32000   160006       96004       -40.00%

benchmark                       old bytes    new bytes   delta
----------------------------------------------------------------
BenchmarkCalcMerkleRoot/20      6896         4432        -35.73%
BenchmarkCalcMerkleRoot/1000    320688       208272      -35.05%
BenchmarkCalcMerkleRoot/2000    641072       416272      -35.07%
BenchmarkCalcMerkleRoot/4000    1281840      832272      -35.07%
BenchmarkCalcMerkleRoot/8000    2563376      1664272     -35.07%
BenchmarkCalcMerkleRoot/16000   5126448      3328272     -35.08%
BenchmarkCalcMerkleRoot/32000   10252592     6656273     -35.08%
2019-08-06 10:40:57 -05:00

98 lines
2.9 KiB
Go

// 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 example demonstrates calculating a merkle root from a slice of leaf
// hashes.
func ExampleCalcMerkleRoot() {
// Create a slice of the leaf hashes.
leaves := make([]chainhash.Hash, 3)
for i := range leaves {
// The hash would ordinarily be calculated from the TxHashFull function
// on a transaction, however, it's left as a zero hash for the purposes
// of this example.
leaves[i] = chainhash.Hash{}
}
merkleRoot := standalone.CalcMerkleRoot(leaves)
fmt.Printf("Result: %s", merkleRoot)
// Output:
// Result: 5fdfcaba377aefc1bfc4af5ef8e0c2a61656e10e8105c4db7656ae5d58f8b77f
}