mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
Merge remaining dcrutil code into a dcrd package.
This merge commit adds the following code from the github.com/decred/dcrutil package into a new github.com/decred/dcrd/dcrutil package: * Address handling * Amount type * AppDataDir func * bitflags functions * Block wrapper type * Hash160 func * Tx wrapper type * WIF type as well as all tests for this code. The old github.com/decred/dcrutil/hdkeychain package has also been merged and moved to github.com/decred/dcrd/dcrutil/hdkeychain. dcrd packages have been updated to use the new packages and the dep files have been updated for this change.
This commit is contained in:
commit
6842aa006d
12
Gopkg.lock
generated
12
Gopkg.lock
generated
@ -57,15 +57,15 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/decred/bitset"
|
||||
name = "github.com/decred/base58"
|
||||
packages = ["."]
|
||||
revision = "484b833245d5f9046e2893a6bd2e54b1df3a53a4"
|
||||
revision = "b3520e187fa8ebe65eb74245408cf4b83e6a65d3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/decred/dcrutil"
|
||||
packages = [".","base58","hdkeychain"]
|
||||
revision = "ddbde93f65ab0692e54ed8a5ad325fa2e8af4daa"
|
||||
name = "github.com/decred/bitset"
|
||||
packages = ["."]
|
||||
revision = "484b833245d5f9046e2893a6bd2e54b1df3a53a4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jessevdk/go-flags"
|
||||
@ -94,6 +94,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "9f7191295caf3e7930cd45f5b3a0c5f4b84611c4cfdcf8c6cc042fcf84c3e904"
|
||||
inputs-digest = "51999d250cc8d4082114b01f3bd8ea4887cd823769dbd41c3ed4c7eeb6c821e5"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
@ -32,11 +32,11 @@
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/decred/bitset"
|
||||
name = "github.com/decred/base58"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/decred/dcrutil"
|
||||
name = "github.com/decred/bitset"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/jessevdk/go-flags"
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// checkCoinbaseUniqueHeight checks to ensure that for all blocks height > 1
|
||||
|
||||
@ -17,9 +17,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// cloneParams returns a deep copy of the provided parameters so the caller is
|
||||
|
||||
@ -17,9 +17,9 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -19,8 +19,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// CheckpointConfirmations is the number of blocks before the end of the current
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// This example demonstrates how to create a new chain instance and use
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/fullblocktests"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// TestFullBlocks ensures all tests generated by the fullblocktests package
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/blockchain/chaingen"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// TestStakeVersion ensures that the stake version field in the block header is
|
||||
|
||||
@ -18,9 +18,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -16,9 +16,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -11,9 +11,9 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// BlockProgressLogger provides periodic logging for other services in order
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"math"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// nextPowerOfTwo returns the next highest power of two from a given number if
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// NotificationType represents the type of a notification message.
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// reorgTestLong does a single, large reorganization.
|
||||
|
||||
@ -10,9 +10,9 @@ import (
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// txValidateItem holds a transaction along with which input to validate.
|
||||
|
||||
@ -8,8 +8,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// SequenceLock represents the minimum timestamp and minimum block height after
|
||||
|
||||
@ -10,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// mustLockTimeToSeq converts the passed relative lock time to a sequence number
|
||||
|
||||
@ -18,9 +18,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// TxType indicates the type of tx (regular or stake type).
|
||||
|
||||
@ -13,9 +13,9 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// SSTX TESTING -------------------------------------------------------------------
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -8,8 +8,8 @@ package blockchain
|
||||
import (
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// NextLotteryData returns the next tickets eligible for spending as SSGen
|
||||
|
||||
@ -12,9 +12,9 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// The number of values to precalculate on initialization of the subsidy
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/blockchain/chaingen"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// StakeViewpoint is the viewpoint of the blockchain depending on stake
|
||||
|
||||
@ -16,9 +16,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -19,9 +19,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// recalculateMsgBlockMerkleRootsSize recalculates the merkle roots for a msgBlock,
|
||||
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// blockProgressLogger provides periodic logging for other services in order
|
||||
|
||||
@ -21,9 +21,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/mempool"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -11,9 +11,9 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// ln2Squared is simply the square of the natural log of 2.
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// TestFilterLarge ensures a maximum sized filter can be created.
|
||||
|
||||
@ -8,8 +8,8 @@ package bloom
|
||||
import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// merkleBlock is used to house intermediate information needed to generate a
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/bloom"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
func TestMerkleBlock3(t *testing.T) {
|
||||
|
||||
@ -24,7 +24,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
)
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
// "fmt"
|
||||
// "log"
|
||||
//
|
||||
// "github.com/decred/dcrutil"
|
||||
// "github.com/decred/dcrd/dcrutil"
|
||||
// "github.com/decred/dcrd/chaincfg"
|
||||
// )
|
||||
//
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/indexers"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var zeroHash = chainhash.Hash{}
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/certgen"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
|
||||
@ -25,9 +25,9 @@ import (
|
||||
"github.com/decred/dcrd/connmgr"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/mempool"
|
||||
"github.com/decred/dcrd/sampleconfig"
|
||||
"github.com/decred/dcrutil"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
)
|
||||
|
||||
|
||||
@ -15,9 +15,9 @@ import (
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/mining"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// importCmd defines the configuration options for the insecureimport command.
|
||||
|
||||
@ -14,8 +14,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
_ "github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// This example demonstrates creating a new database.
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// BenchmarkBlockHeader benchmarks how long it takes to load the mainnet genesis
|
||||
|
||||
@ -25,8 +25,8 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/database/internal/treap"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/database/ffldb"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// dbType is the database type name for this driver.
|
||||
|
||||
@ -27,8 +27,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -22,8 +22,8 @@ import (
|
||||
"github.com/btcsuite/goleveldb/leveldb"
|
||||
ldberrors "github.com/btcsuite/goleveldb/leveldb/errors"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -10,7 +10,7 @@ package database
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// Cursor represents a cursor over key/value pairs and nested buckets of a
|
||||
|
||||
26
dcrutil/README.md
Normal file
26
dcrutil/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
dcrutil
|
||||
=======
|
||||
|
||||
|
||||
[](https://travis-ci.org/decred/dcrd)
|
||||
[](http://copyfree.org)
|
||||
[](http://godoc.org/github.com/decred/dcrd/dcrutil)
|
||||
|
||||
Package dcrutil provides decred-specific convenience functions and types.
|
||||
A comprehensive suite of tests is provided to ensure proper functionality.
|
||||
|
||||
This package was developed for dcrd, a full-node implementation of Decred which
|
||||
is under active development by Company 0. Although it was primarily written for
|
||||
dcrd, this package has intentionally been designed so it can be used as a
|
||||
standalone package for any projects needing the functionality provided.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/decred/dcrd/dcrutil
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package dcrutil is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
||||
756
dcrutil/address.go
Normal file
756
dcrutil/address.go
Normal file
@ -0,0 +1,756 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/decred/base58"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrChecksumMismatch describes an error where decoding failed due
|
||||
// to a bad checksum.
|
||||
ErrChecksumMismatch = errors.New("checksum mismatch")
|
||||
|
||||
// ErrUnknownAddressType describes an error where an address can not
|
||||
// decoded as a specific address type due to the string encoding
|
||||
// begining with an identifier byte unknown to any standard or
|
||||
// registered (via chaincfg.Register) network.
|
||||
ErrUnknownAddressType = errors.New("unknown address type")
|
||||
|
||||
// ErrAddressCollision describes an error where an address can not
|
||||
// be uniquely determined as either a pay-to-pubkey-hash or
|
||||
// pay-to-script-hash address since the leading identifier is used for
|
||||
// describing both address kinds, but for different networks. Rather
|
||||
// than assuming or defaulting to one or the other, this error is
|
||||
// returned and the caller must decide how to decode the address.
|
||||
ErrAddressCollision = errors.New("address collision")
|
||||
|
||||
// ErrMissingDefaultNet describes an error in DecodeAddress that
|
||||
// attempts to decode an address without defining which network to decode
|
||||
// for.
|
||||
ErrMissingDefaultNet = errors.New("default net not defined")
|
||||
)
|
||||
|
||||
// encodeAddress returns a human-readable payment address given a ripemd160 hash
|
||||
// and netID which encodes the network and address type. It is used in both
|
||||
// pay-to-pubkey-hash (P2PKH) and pay-to-script-hash (P2SH) address encoding.
|
||||
func encodeAddress(hash160 []byte, netID [2]byte) string {
|
||||
// Format is 2 bytes for a network and address class (i.e. P2PKH vs
|
||||
// P2SH), 20 bytes for a RIPEMD160 hash, and 4 bytes of checksum.
|
||||
return base58.CheckEncode(hash160[:ripemd160.Size], netID)
|
||||
}
|
||||
|
||||
// encodePKAddress returns a human-readable payment address to a public key
|
||||
// given a serialized public key, a netID, and a signature suite.
|
||||
func encodePKAddress(serializedPK []byte, netID [2]byte, algo int) string {
|
||||
pubKeyBytes := []byte{0x00}
|
||||
|
||||
switch algo {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
pubKeyBytes[0] = byte(chainec.ECTypeSecp256k1)
|
||||
case chainec.ECTypeEdwards:
|
||||
pubKeyBytes[0] = byte(chainec.ECTypeEdwards)
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
pubKeyBytes[0] = byte(chainec.ECTypeSecSchnorr)
|
||||
}
|
||||
|
||||
// Pubkeys are encoded as [0] = type/ybit, [1:33] = serialized pubkey
|
||||
compressed := serializedPK
|
||||
if algo == chainec.ECTypeSecp256k1 || algo == chainec.ECTypeSecSchnorr {
|
||||
pub, err := chainec.Secp256k1.ParsePubKey(serializedPK)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
pubSerComp := pub.SerializeCompressed()
|
||||
|
||||
// Set the y-bit if needed.
|
||||
if pubSerComp[0] == 0x03 {
|
||||
pubKeyBytes[0] |= (1 << 7)
|
||||
}
|
||||
|
||||
compressed = pubSerComp[1:]
|
||||
}
|
||||
|
||||
pubKeyBytes = append(pubKeyBytes, compressed...)
|
||||
return base58.CheckEncode(pubKeyBytes, netID)
|
||||
}
|
||||
|
||||
// Address is an interface type for any type of destination a transaction
|
||||
// output may spend to. This includes pay-to-pubkey (P2PK), pay-to-pubkey-hash
|
||||
// (P2PKH), and pay-to-script-hash (P2SH). Address is designed to be generic
|
||||
// enough that other kinds of addresses may be added in the future without
|
||||
// changing the decoding and encoding API.
|
||||
type Address interface {
|
||||
// String returns the string encoding of the transaction output
|
||||
// destination.
|
||||
//
|
||||
// Please note that String differs subtly from EncodeAddress: String
|
||||
// will return the value as a string without any conversion, while
|
||||
// EncodeAddress may convert destination types (for example,
|
||||
// converting pubkeys to P2PKH addresses) before encoding as a
|
||||
// payment address string.
|
||||
String() string
|
||||
|
||||
// EncodeAddress returns the string encoding of the payment address
|
||||
// associated with the Address value. See the comment on String
|
||||
// for how this method differs from String.
|
||||
EncodeAddress() string
|
||||
|
||||
// ScriptAddress returns the raw bytes of the address to be used
|
||||
// when inserting the address into a txout's script.
|
||||
ScriptAddress() []byte
|
||||
|
||||
// Hash160 returns the Hash160(data) where data is the data normally
|
||||
// hashed to 160 bits from the respective address type.
|
||||
Hash160() *[ripemd160.Size]byte
|
||||
|
||||
// IsForNet returns whether or not the address is associated with the
|
||||
// passed network.
|
||||
IsForNet(*chaincfg.Params) bool
|
||||
|
||||
// DSA returns the digital signature algorithm for the address.
|
||||
DSA(*chaincfg.Params) int
|
||||
|
||||
// Net returns the network parameters of the address.
|
||||
Net() *chaincfg.Params
|
||||
}
|
||||
|
||||
// NewAddressPubKey returns a new Address. decoded must
|
||||
// be 33 bytes.
|
||||
func NewAddressPubKey(decoded []byte, net *chaincfg.Params) (Address, error) {
|
||||
if len(decoded) == 33 {
|
||||
// First byte is the signature suite and ybit.
|
||||
suite := decoded[0]
|
||||
suite &= ^uint8(1 << 7)
|
||||
ybit := !(decoded[0]&(1<<7) == 0)
|
||||
toAppend := uint8(0x02)
|
||||
if ybit {
|
||||
toAppend = 0x03
|
||||
}
|
||||
|
||||
switch int(suite) {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
return NewAddressSecpPubKey(
|
||||
append([]byte{toAppend}, decoded[1:]...),
|
||||
net)
|
||||
case chainec.ECTypeEdwards:
|
||||
return NewAddressEdwardsPubKey(decoded, net)
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
return NewAddressSecSchnorrPubKey(
|
||||
append([]byte{toAppend}, decoded[1:]...),
|
||||
net)
|
||||
}
|
||||
return nil, ErrUnknownAddressType
|
||||
}
|
||||
return nil, ErrUnknownAddressType
|
||||
}
|
||||
|
||||
// DecodeAddress decodes the string encoding of an address and returns
|
||||
// the Address if addr is a valid encoding for a known address type
|
||||
func DecodeAddress(addr string) (Address, error) {
|
||||
// Switch on decoded length to determine the type.
|
||||
decoded, netID, err := base58.CheckDecode(addr)
|
||||
if err != nil {
|
||||
if err == base58.ErrChecksum {
|
||||
return nil, ErrChecksumMismatch
|
||||
}
|
||||
return nil, fmt.Errorf("decoded address is of unknown format: %v",
|
||||
err.Error())
|
||||
}
|
||||
|
||||
net, err := detectNetworkForAddress(addr)
|
||||
if err != nil {
|
||||
return nil, ErrUnknownAddressType
|
||||
}
|
||||
|
||||
switch netID {
|
||||
case net.PubKeyAddrID:
|
||||
return NewAddressPubKey(decoded, net)
|
||||
|
||||
case net.PubKeyHashAddrID:
|
||||
return NewAddressPubKeyHash(decoded, net, chainec.ECTypeSecp256k1)
|
||||
|
||||
case net.PKHEdwardsAddrID:
|
||||
return NewAddressPubKeyHash(decoded, net, chainec.ECTypeEdwards)
|
||||
|
||||
case net.PKHSchnorrAddrID:
|
||||
return NewAddressPubKeyHash(decoded, net, chainec.ECTypeSecSchnorr)
|
||||
|
||||
case net.ScriptHashAddrID:
|
||||
return NewAddressScriptHashFromHash(decoded, net)
|
||||
|
||||
default:
|
||||
return nil, ErrUnknownAddressType
|
||||
}
|
||||
}
|
||||
|
||||
// detectNetworkForAddress pops the first character from a string encoded
|
||||
// address and detects what network type it is for.
|
||||
func detectNetworkForAddress(addr string) (*chaincfg.Params, error) {
|
||||
if len(addr) < 1 {
|
||||
return nil, fmt.Errorf("empty string given for network detection")
|
||||
}
|
||||
|
||||
networkChar := addr[0:1]
|
||||
switch networkChar {
|
||||
case chaincfg.MainNetParams.NetworkAddressPrefix:
|
||||
return &chaincfg.MainNetParams, nil
|
||||
case chaincfg.TestNet2Params.NetworkAddressPrefix:
|
||||
return &chaincfg.TestNet2Params, nil
|
||||
case chaincfg.SimNetParams.NetworkAddressPrefix:
|
||||
return &chaincfg.SimNetParams, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown network type in string encoded address")
|
||||
}
|
||||
|
||||
// AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH)
|
||||
// transaction.
|
||||
type AddressPubKeyHash struct {
|
||||
net *chaincfg.Params
|
||||
hash [ripemd160.Size]byte
|
||||
netID [2]byte
|
||||
}
|
||||
|
||||
// NewAddressPubKeyHash returns a new AddressPubKeyHash. pkHash must
|
||||
// be 20 bytes.
|
||||
func NewAddressPubKeyHash(pkHash []byte, net *chaincfg.Params,
|
||||
algo int) (*AddressPubKeyHash, error) {
|
||||
var addrID [2]byte
|
||||
switch algo {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
addrID = net.PubKeyHashAddrID
|
||||
case chainec.ECTypeEdwards:
|
||||
addrID = net.PKHEdwardsAddrID
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
addrID = net.PKHSchnorrAddrID
|
||||
default:
|
||||
return nil, errors.New("unknown ECDSA algorithm")
|
||||
}
|
||||
apkh, err := newAddressPubKeyHash(pkHash, addrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apkh.net = net
|
||||
return apkh, nil
|
||||
}
|
||||
|
||||
// newAddressPubKeyHash is the internal API to create a pubkey hash address
|
||||
// with a known leading identifier byte for a network, rather than looking
|
||||
// it up through its parameters. This is useful when creating a new address
|
||||
// structure from a string encoding where the identifer byte is already
|
||||
// known.
|
||||
func newAddressPubKeyHash(pkHash []byte, netID [2]byte) (*AddressPubKeyHash,
|
||||
error) {
|
||||
// Check for a valid pubkey hash length.
|
||||
if len(pkHash) != ripemd160.Size {
|
||||
return nil, errors.New("pkHash must be 20 bytes")
|
||||
}
|
||||
addr := &AddressPubKeyHash{netID: netID}
|
||||
copy(addr.hash[:], pkHash)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// EncodeAddress returns the string encoding of a pay-to-pubkey-hash
|
||||
// address. Part of the Address interface.
|
||||
func (a *AddressPubKeyHash) EncodeAddress() string {
|
||||
return encodeAddress(a.hash[:], a.netID)
|
||||
}
|
||||
|
||||
// ScriptAddress returns the bytes to be included in a txout script to pay
|
||||
// to a pubkey hash. Part of the Address interface.
|
||||
func (a *AddressPubKeyHash) ScriptAddress() []byte {
|
||||
return a.hash[:]
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the pay-to-pubkey-hash address is associated
|
||||
// with the passed network.
|
||||
func (a *AddressPubKeyHash) IsForNet(net *chaincfg.Params) bool {
|
||||
return a.netID == net.PubKeyHashAddrID ||
|
||||
a.netID == net.PKHEdwardsAddrID ||
|
||||
a.netID == net.PKHSchnorrAddrID
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the pay-to-pubkey-hash address.
|
||||
// This is equivalent to calling EncodeAddress, but is provided so the type can
|
||||
// be used as a fmt.Stringer.
|
||||
func (a *AddressPubKeyHash) String() string {
|
||||
return a.EncodeAddress()
|
||||
}
|
||||
|
||||
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
||||
// when an array is more appropiate than a slice (for example, when used as map
|
||||
// keys).
|
||||
func (a *AddressPubKeyHash) Hash160() *[ripemd160.Size]byte {
|
||||
return &a.hash
|
||||
}
|
||||
|
||||
// DSA returns the digital signature algorithm for the associated public key
|
||||
// hash.
|
||||
func (a *AddressPubKeyHash) DSA(net *chaincfg.Params) int {
|
||||
switch a.netID {
|
||||
case net.PubKeyHashAddrID:
|
||||
return chainec.ECTypeSecp256k1
|
||||
case net.PKHEdwardsAddrID:
|
||||
return chainec.ECTypeEdwards
|
||||
case net.PKHSchnorrAddrID:
|
||||
return chainec.ECTypeSecSchnorr
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Net returns the network for the address.
|
||||
func (a *AddressPubKeyHash) Net() *chaincfg.Params {
|
||||
return a.net
|
||||
}
|
||||
|
||||
// AddressScriptHash is an Address for a pay-to-script-hash (P2SH)
|
||||
// transaction.
|
||||
type AddressScriptHash struct {
|
||||
net *chaincfg.Params
|
||||
hash [ripemd160.Size]byte
|
||||
netID [2]byte
|
||||
}
|
||||
|
||||
// NewAddressScriptHash returns a new AddressScriptHash.
|
||||
func NewAddressScriptHash(serializedScript []byte,
|
||||
net *chaincfg.Params) (*AddressScriptHash, error) {
|
||||
scriptHash := Hash160(serializedScript)
|
||||
ash, err := newAddressScriptHashFromHash(scriptHash, net.ScriptHashAddrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ash.net = net
|
||||
|
||||
return ash, nil
|
||||
}
|
||||
|
||||
// NewAddressScriptHashFromHash returns a new AddressScriptHash. scriptHash
|
||||
// must be 20 bytes.
|
||||
func NewAddressScriptHashFromHash(scriptHash []byte,
|
||||
net *chaincfg.Params) (*AddressScriptHash, error) {
|
||||
ash, err := newAddressScriptHashFromHash(scriptHash, net.ScriptHashAddrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ash.net = net
|
||||
|
||||
return ash, nil
|
||||
}
|
||||
|
||||
// newAddressScriptHashFromHash is the internal API to create a script hash
|
||||
// address with a known leading identifier byte for a network, rather than
|
||||
// looking it up through its parameters. This is useful when creating a new
|
||||
// address structure from a string encoding where the identifer byte is already
|
||||
// known.
|
||||
func newAddressScriptHashFromHash(scriptHash []byte,
|
||||
netID [2]byte) (*AddressScriptHash, error) {
|
||||
// Check for a valid script hash length.
|
||||
if len(scriptHash) != ripemd160.Size {
|
||||
return nil, errors.New("scriptHash must be 20 bytes")
|
||||
}
|
||||
|
||||
addr := &AddressScriptHash{netID: netID}
|
||||
copy(addr.hash[:], scriptHash)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// EncodeAddress returns the string encoding of a pay-to-script-hash
|
||||
// address. Part of the Address interface.
|
||||
func (a *AddressScriptHash) EncodeAddress() string {
|
||||
return encodeAddress(a.hash[:], a.netID)
|
||||
}
|
||||
|
||||
// ScriptAddress returns the bytes to be included in a txout script to pay
|
||||
// to a script hash. Part of the Address interface.
|
||||
func (a *AddressScriptHash) ScriptAddress() []byte {
|
||||
return a.hash[:]
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the pay-to-script-hash address is associated
|
||||
// with the passed network.
|
||||
func (a *AddressScriptHash) IsForNet(net *chaincfg.Params) bool {
|
||||
return a.netID == net.ScriptHashAddrID
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the pay-to-script-hash address.
|
||||
// This is equivalent to calling EncodeAddress, but is provided so the type can
|
||||
// be used as a fmt.Stringer.
|
||||
func (a *AddressScriptHash) String() string {
|
||||
return a.EncodeAddress()
|
||||
}
|
||||
|
||||
// Hash160 returns the underlying array of the script hash. This can be useful
|
||||
// when an array is more appropiate than a slice (for example, when used as map
|
||||
// keys).
|
||||
func (a *AddressScriptHash) Hash160() *[ripemd160.Size]byte {
|
||||
return &a.hash
|
||||
}
|
||||
|
||||
// DSA returns -1 (invalid) as the digital signature algorithm for scripts,
|
||||
// as scripts may not involve digital signatures at all.
|
||||
func (a *AddressScriptHash) DSA(net *chaincfg.Params) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Net returns the network for the address.
|
||||
func (a *AddressScriptHash) Net() *chaincfg.Params {
|
||||
return a.net
|
||||
}
|
||||
|
||||
// PubKeyFormat describes what format to use for a pay-to-pubkey address.
|
||||
type PubKeyFormat int
|
||||
|
||||
const (
|
||||
// PKFUncompressed indicates the pay-to-pubkey address format is an
|
||||
// uncompressed public key.
|
||||
PKFUncompressed PubKeyFormat = iota
|
||||
|
||||
// PKFCompressed indicates the pay-to-pubkey address format is a
|
||||
// compressed public key.
|
||||
PKFCompressed
|
||||
|
||||
// PKFHybrid indicates the pay-to-pubkey address format is a hybrid
|
||||
// public key.
|
||||
PKFHybrid
|
||||
)
|
||||
|
||||
// AddressSecpPubKey is an Address for a secp256k1 pay-to-pubkey transaction.
|
||||
type AddressSecpPubKey struct {
|
||||
net *chaincfg.Params
|
||||
pubKeyFormat PubKeyFormat
|
||||
pubKey chainec.PublicKey
|
||||
pubKeyHashID [2]byte
|
||||
}
|
||||
|
||||
// NewAddressSecpPubKey returns a new AddressSecpPubKey which represents a
|
||||
// pay-to-pubkey address, using a secp256k1 pubkey. The serializedPubKey
|
||||
// parameter must be a valid pubkey and can be uncompressed, compressed, or
|
||||
// hybrid.
|
||||
func NewAddressSecpPubKey(serializedPubKey []byte,
|
||||
net *chaincfg.Params) (*AddressSecpPubKey, error) {
|
||||
pubKey, err := chainec.Secp256k1.ParsePubKey(serializedPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set the format of the pubkey. This probably should be returned
|
||||
// from dcrec, but do it here to avoid API churn. We already know the
|
||||
// pubkey is valid since it parsed above, so it's safe to simply examine
|
||||
// the leading byte to get the format.
|
||||
pkFormat := PKFUncompressed
|
||||
switch serializedPubKey[0] {
|
||||
case 0x02, 0x03:
|
||||
pkFormat = PKFCompressed
|
||||
case 0x06, 0x07:
|
||||
pkFormat = PKFHybrid
|
||||
}
|
||||
|
||||
return &AddressSecpPubKey{
|
||||
net: net,
|
||||
pubKeyFormat: pkFormat,
|
||||
pubKey: pubKey,
|
||||
pubKeyHashID: net.PubKeyHashAddrID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// serialize returns the serialization of the public key according to the
|
||||
// format associated with the address.
|
||||
func (a *AddressSecpPubKey) serialize() []byte {
|
||||
switch a.pubKeyFormat {
|
||||
default:
|
||||
fallthrough
|
||||
case PKFUncompressed:
|
||||
return a.pubKey.SerializeUncompressed()
|
||||
|
||||
case PKFCompressed:
|
||||
return a.pubKey.SerializeCompressed()
|
||||
|
||||
case PKFHybrid:
|
||||
return a.pubKey.SerializeHybrid()
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeAddress returns the string encoding of the public key as a
|
||||
// pay-to-pubkey-hash. Note that the public key format (uncompressed,
|
||||
// compressed, etc) will change the resulting address. This is expected since
|
||||
// pay-to-pubkey-hash is a hash of the serialized public key which obviously
|
||||
// differs with the format. At the time of this writing, most Decred addresses
|
||||
// are pay-to-pubkey-hash constructed from the uncompressed public key.
|
||||
//
|
||||
// Part of the Address interface.
|
||||
func (a *AddressSecpPubKey) EncodeAddress() string {
|
||||
return encodeAddress(Hash160(a.serialize()), a.pubKeyHashID)
|
||||
}
|
||||
|
||||
// ScriptAddress returns the bytes to be included in a txout script to pay
|
||||
// to a public key. Setting the public key format will affect the output of
|
||||
// this function accordingly. Part of the Address interface.
|
||||
func (a *AddressSecpPubKey) ScriptAddress() []byte {
|
||||
return a.serialize()
|
||||
}
|
||||
|
||||
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
||||
// when an array is more appropiate than a slice (for example, when used as map
|
||||
// keys).
|
||||
func (a *AddressSecpPubKey) Hash160() *[ripemd160.Size]byte {
|
||||
h160 := Hash160(a.pubKey.SerializeCompressed())
|
||||
array := new([ripemd160.Size]byte)
|
||||
copy(array[:], h160)
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the pay-to-pubkey address is associated
|
||||
// with the passed network.
|
||||
func (a *AddressSecpPubKey) IsForNet(net *chaincfg.Params) bool {
|
||||
return a.pubKeyHashID == net.PubKeyHashAddrID
|
||||
}
|
||||
|
||||
// String returns the hex-encoded human-readable string for the pay-to-pubkey
|
||||
// address. This is not the same as calling EncodeAddress.
|
||||
func (a *AddressSecpPubKey) String() string {
|
||||
return encodePKAddress(a.serialize(), a.net.PubKeyAddrID,
|
||||
chainec.ECTypeSecp256k1)
|
||||
}
|
||||
|
||||
// Format returns the format (uncompressed, compressed, etc) of the
|
||||
// pay-to-pubkey address.
|
||||
func (a *AddressSecpPubKey) Format() PubKeyFormat {
|
||||
return a.pubKeyFormat
|
||||
}
|
||||
|
||||
// AddressPubKeyHash returns the pay-to-pubkey address converted to a
|
||||
// pay-to-pubkey-hash address. Note that the public key format (uncompressed,
|
||||
// compressed, etc) will change the resulting address. This is expected since
|
||||
// pay-to-pubkey-hash is a hash of the serialized public key which obviously
|
||||
// differs with the format. At the time of this writing, most Decred addresses
|
||||
// are pay-to-pubkey-hash constructed from the uncompressed public key.
|
||||
func (a *AddressSecpPubKey) AddressPubKeyHash() *AddressPubKeyHash {
|
||||
addr := &AddressPubKeyHash{net: a.net, netID: a.pubKeyHashID}
|
||||
copy(addr.hash[:], Hash160(a.serialize()))
|
||||
return addr
|
||||
}
|
||||
|
||||
// PubKey returns the underlying public key for the address.
|
||||
func (a *AddressSecpPubKey) PubKey() chainec.PublicKey {
|
||||
return a.pubKey
|
||||
}
|
||||
|
||||
// DSA returns the underlying digital signature algorithm for the
|
||||
// address.
|
||||
func (a *AddressSecpPubKey) DSA(net *chaincfg.Params) int {
|
||||
switch a.pubKeyHashID {
|
||||
case net.PubKeyHashAddrID:
|
||||
return chainec.ECTypeSecp256k1
|
||||
case net.PKHSchnorrAddrID:
|
||||
return chainec.ECTypeSecSchnorr
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Net returns the network for the address.
|
||||
func (a *AddressSecpPubKey) Net() *chaincfg.Params {
|
||||
return a.net
|
||||
}
|
||||
|
||||
// NewAddressSecpPubKeyCompressed creates a new address using a compressed public key
|
||||
func NewAddressSecpPubKeyCompressed(pubkey chainec.PublicKey, params *chaincfg.Params) (*AddressSecpPubKey, error) {
|
||||
return NewAddressSecpPubKey(pubkey.SerializeCompressed(), params)
|
||||
}
|
||||
|
||||
// AddressEdwardsPubKey is an Address for an Ed25519 pay-to-pubkey transaction.
|
||||
type AddressEdwardsPubKey struct {
|
||||
net *chaincfg.Params
|
||||
pubKey chainec.PublicKey
|
||||
pubKeyHashID [2]byte
|
||||
}
|
||||
|
||||
// NewAddressEdwardsPubKey returns a new AddressEdwardsPubKey which represents a
|
||||
// pay-to-pubkey address, using an Ed25519 pubkey. The serializedPubKey
|
||||
// parameter must be a valid 32 byte serialized public key.
|
||||
func NewAddressEdwardsPubKey(serializedPubKey []byte,
|
||||
net *chaincfg.Params) (*AddressEdwardsPubKey, error) {
|
||||
pubKey, err := chainec.Edwards.ParsePubKey(serializedPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddressEdwardsPubKey{
|
||||
net: net,
|
||||
pubKey: pubKey,
|
||||
pubKeyHashID: net.PKHEdwardsAddrID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// serialize returns the serialization of the public key.
|
||||
func (a *AddressEdwardsPubKey) serialize() []byte {
|
||||
return a.pubKey.Serialize()
|
||||
}
|
||||
|
||||
// EncodeAddress returns the string encoding of the public key as a
|
||||
// pay-to-pubkey-hash.
|
||||
//
|
||||
// Part of the Address interface.
|
||||
func (a *AddressEdwardsPubKey) EncodeAddress() string {
|
||||
return encodeAddress(Hash160(a.serialize()), a.pubKeyHashID)
|
||||
}
|
||||
|
||||
// ScriptAddress returns the bytes to be included in a txout script to pay
|
||||
// to a public key. Setting the public key format will affect the output of
|
||||
// this function accordingly. Part of the Address interface.
|
||||
func (a *AddressEdwardsPubKey) ScriptAddress() []byte {
|
||||
return a.serialize()
|
||||
}
|
||||
|
||||
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
||||
// when an array is more appropiate than a slice (for example, when used as map
|
||||
// keys).
|
||||
func (a *AddressEdwardsPubKey) Hash160() *[ripemd160.Size]byte {
|
||||
h160 := Hash160(a.pubKey.Serialize())
|
||||
array := new([ripemd160.Size]byte)
|
||||
copy(array[:], h160)
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the pay-to-pubkey address is associated
|
||||
// with the passed network.
|
||||
func (a *AddressEdwardsPubKey) IsForNet(net *chaincfg.Params) bool {
|
||||
return a.pubKeyHashID == net.PKHEdwardsAddrID
|
||||
}
|
||||
|
||||
// String returns the hex-encoded human-readable string for the pay-to-pubkey
|
||||
// address. This is not the same as calling EncodeAddress.
|
||||
func (a *AddressEdwardsPubKey) String() string {
|
||||
return encodePKAddress(a.serialize(), a.net.PubKeyAddrID,
|
||||
chainec.ECTypeEdwards)
|
||||
}
|
||||
|
||||
// AddressPubKeyHash returns the pay-to-pubkey address converted to a
|
||||
// pay-to-pubkey-hash address.
|
||||
func (a *AddressEdwardsPubKey) AddressPubKeyHash() *AddressPubKeyHash {
|
||||
addr := &AddressPubKeyHash{net: a.net, netID: a.pubKeyHashID}
|
||||
copy(addr.hash[:], Hash160(a.serialize()))
|
||||
return addr
|
||||
}
|
||||
|
||||
// PubKey returns the underlying public key for the address.
|
||||
func (a *AddressEdwardsPubKey) PubKey() chainec.PublicKey {
|
||||
return a.pubKey
|
||||
}
|
||||
|
||||
// DSA returns the underlying digital signature algorithm for the
|
||||
// address.
|
||||
func (a *AddressEdwardsPubKey) DSA(net *chaincfg.Params) int {
|
||||
return chainec.ECTypeEdwards
|
||||
}
|
||||
|
||||
// Net returns the network for the address.
|
||||
func (a *AddressEdwardsPubKey) Net() *chaincfg.Params {
|
||||
return a.net
|
||||
}
|
||||
|
||||
// AddressSecSchnorrPubKey is an Address for a secp256k1 pay-to-pubkey
|
||||
// transaction.
|
||||
type AddressSecSchnorrPubKey struct {
|
||||
net *chaincfg.Params
|
||||
pubKey chainec.PublicKey
|
||||
pubKeyHashID [2]byte
|
||||
}
|
||||
|
||||
// NewAddressSecSchnorrPubKey returns a new AddressSecpPubKey which represents a
|
||||
// pay-to-pubkey address, using a secp256k1 pubkey. The serializedPubKey
|
||||
// parameter must be a valid pubkey and can be uncompressed, compressed, or
|
||||
// hybrid.
|
||||
func NewAddressSecSchnorrPubKey(serializedPubKey []byte,
|
||||
net *chaincfg.Params) (*AddressSecSchnorrPubKey, error) {
|
||||
pubKey, err := chainec.SecSchnorr.ParsePubKey(serializedPubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddressSecSchnorrPubKey{
|
||||
net: net,
|
||||
pubKey: pubKey,
|
||||
pubKeyHashID: net.PKHSchnorrAddrID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// serialize returns the serialization of the public key according to the
|
||||
// format associated with the address.
|
||||
func (a *AddressSecSchnorrPubKey) serialize() []byte {
|
||||
return a.pubKey.Serialize()
|
||||
}
|
||||
|
||||
// EncodeAddress returns the string encoding of the public key as a
|
||||
// pay-to-pubkey-hash. Note that the public key format (uncompressed,
|
||||
// compressed, etc) will change the resulting address. This is expected since
|
||||
// pay-to-pubkey-hash is a hash of the serialized public key which obviously
|
||||
// differs with the format. At the time of this writing, most Decred addresses
|
||||
// are pay-to-pubkey-hash constructed from the uncompressed public key.
|
||||
//
|
||||
// Part of the Address interface.
|
||||
func (a *AddressSecSchnorrPubKey) EncodeAddress() string {
|
||||
return encodeAddress(Hash160(a.serialize()), a.pubKeyHashID)
|
||||
}
|
||||
|
||||
// ScriptAddress returns the bytes to be included in a txout script to pay
|
||||
// to a public key. Setting the public key format will affect the output of
|
||||
// this function accordingly. Part of the Address interface.
|
||||
func (a *AddressSecSchnorrPubKey) ScriptAddress() []byte {
|
||||
return a.serialize()
|
||||
}
|
||||
|
||||
// Hash160 returns the underlying array of the pubkey hash. This can be useful
|
||||
// when an array is more appropiate than a slice (for example, when used as map
|
||||
// keys).
|
||||
func (a *AddressSecSchnorrPubKey) Hash160() *[ripemd160.Size]byte {
|
||||
h160 := Hash160(a.pubKey.Serialize())
|
||||
array := new([ripemd160.Size]byte)
|
||||
copy(array[:], h160)
|
||||
|
||||
return array
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the pay-to-pubkey address is associated
|
||||
// with the passed network.
|
||||
func (a *AddressSecSchnorrPubKey) IsForNet(net *chaincfg.Params) bool {
|
||||
return a.pubKeyHashID == net.PubKeyHashAddrID
|
||||
}
|
||||
|
||||
// String returns the hex-encoded human-readable string for the pay-to-pubkey
|
||||
// address. This is not the same as calling EncodeAddress.
|
||||
func (a *AddressSecSchnorrPubKey) String() string {
|
||||
return encodePKAddress(a.serialize(), a.net.PubKeyAddrID,
|
||||
chainec.ECTypeSecSchnorr)
|
||||
}
|
||||
|
||||
// AddressPubKeyHash returns the pay-to-pubkey address converted to a
|
||||
// pay-to-pubkey-hash address.
|
||||
func (a *AddressSecSchnorrPubKey) AddressPubKeyHash() *AddressPubKeyHash {
|
||||
addr := &AddressPubKeyHash{net: a.net, netID: a.pubKeyHashID}
|
||||
copy(addr.hash[:], Hash160(a.serialize()))
|
||||
return addr
|
||||
}
|
||||
|
||||
// DSA returns the underlying digital signature algorithm for the
|
||||
// address.
|
||||
func (a *AddressSecSchnorrPubKey) DSA(net *chaincfg.Params) int {
|
||||
return chainec.ECTypeSecSchnorr
|
||||
}
|
||||
|
||||
// Net returns the network for the address.
|
||||
func (a *AddressSecSchnorrPubKey) Net() *chaincfg.Params {
|
||||
return a.net
|
||||
}
|
||||
579
dcrutil/address_test.go
Normal file
579
dcrutil/address_test.go
Normal file
@ -0,0 +1,579 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
// invalidNet is an invalid network.
|
||||
const invalidNet = wire.CurrencyNet(0xffffffff)
|
||||
|
||||
func TestAddresses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
addr string
|
||||
saddr string
|
||||
encoded string
|
||||
valid bool
|
||||
result dcrutil.Address
|
||||
f func() (dcrutil.Address, error)
|
||||
net *chaincfg.Params
|
||||
}{
|
||||
// Positive P2PKH tests.
|
||||
{
|
||||
name: "mainnet p2pkh",
|
||||
addr: "DsUZxxoHJSty8DCfwfartwTYbuhmVct7tJu",
|
||||
encoded: "DsUZxxoHJSty8DCfwfartwTYbuhmVct7tJu",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKeyHash(
|
||||
[ripemd160.Size]byte{
|
||||
0x27, 0x89, 0xd5, 0x8c, 0xfa, 0x09, 0x57, 0xd2, 0x06, 0xf0,
|
||||
0x25, 0xc2, 0xaf, 0x05, 0x6f, 0xc8, 0xa7, 0x7c, 0xeb, 0xb0},
|
||||
|
||||
chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
pkHash := []byte{
|
||||
0x27, 0x89, 0xd5, 0x8c, 0xfa, 0x09, 0x57, 0xd2, 0x06, 0xf0,
|
||||
0x25, 0xc2, 0xaf, 0x05, 0x6f, 0xc8, 0xa7, 0x7c, 0xeb, 0xb0}
|
||||
return dcrutil.NewAddressPubKeyHash(pkHash,
|
||||
&chaincfg.MainNetParams, chainec.ECTypeSecp256k1)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "mainnet p2pkh 2",
|
||||
addr: "DsU7xcg53nxaKLLcAUSKyRndjG78Z2VZnX9",
|
||||
encoded: "DsU7xcg53nxaKLLcAUSKyRndjG78Z2VZnX9",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKeyHash(
|
||||
[ripemd160.Size]byte{
|
||||
0x22, 0x9e, 0xba, 0xc3, 0x0e, 0xfd, 0x6a, 0x69, 0xee, 0xc9,
|
||||
0xc1, 0xa4, 0x8e, 0x04, 0x8b, 0x7c, 0x97, 0x5c, 0x25, 0xf2},
|
||||
chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
pkHash := []byte{
|
||||
0x22, 0x9e, 0xba, 0xc3, 0x0e, 0xfd, 0x6a, 0x69, 0xee, 0xc9,
|
||||
0xc1, 0xa4, 0x8e, 0x04, 0x8b, 0x7c, 0x97, 0x5c, 0x25, 0xf2}
|
||||
return dcrutil.NewAddressPubKeyHash(pkHash,
|
||||
&chaincfg.MainNetParams, chainec.ECTypeSecp256k1)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pkh",
|
||||
addr: "Tso2MVTUeVrjHTBFedFhiyM7yVTbieqp91h",
|
||||
encoded: "Tso2MVTUeVrjHTBFedFhiyM7yVTbieqp91h",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKeyHash(
|
||||
[ripemd160.Size]byte{
|
||||
0xf1, 0x5d, 0xa1, 0xcb, 0x8d, 0x1b, 0xcb, 0x16, 0x2c, 0x6a,
|
||||
0xb4, 0x46, 0xc9, 0x57, 0x57, 0xa6, 0xe7, 0x91, 0xc9, 0x16},
|
||||
chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
pkHash := []byte{
|
||||
0xf1, 0x5d, 0xa1, 0xcb, 0x8d, 0x1b, 0xcb, 0x16, 0x2c, 0x6a,
|
||||
0xb4, 0x46, 0xc9, 0x57, 0x57, 0xa6, 0xe7, 0x91, 0xc9, 0x16}
|
||||
return dcrutil.NewAddressPubKeyHash(pkHash,
|
||||
&chaincfg.TestNet2Params, chainec.ECTypeSecp256k1)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
|
||||
// Negative P2PKH tests.
|
||||
{
|
||||
name: "p2pkh wrong hash length",
|
||||
addr: "",
|
||||
valid: false,
|
||||
f: func() (dcrutil.Address, error) {
|
||||
pkHash := []byte{
|
||||
0x00, 0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b,
|
||||
0xf4, 0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad,
|
||||
0xaa}
|
||||
return dcrutil.NewAddressPubKeyHash(pkHash,
|
||||
&chaincfg.MainNetParams,
|
||||
chainec.ECTypeSecp256k1)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "p2pkh bad checksum",
|
||||
addr: "TsmWaPM77WSyA3aiQ2Q1KnwGDVWvEkhip23",
|
||||
valid: false,
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
|
||||
// Positive P2SH tests.
|
||||
{
|
||||
// Taken from transactions:
|
||||
// output: 3c9018e8d5615c306d72397f8f5eef44308c98fb576a88e030c25456b4f3a7ac
|
||||
// input: 837dea37ddc8b1e3ce646f1a656e79bbd8cc7f558ac56a169626d649ebe2a3ba.
|
||||
name: "mainnet p2sh",
|
||||
addr: "DcuQKx8BES9wU7C6Q5VmLBjw436r27hayjS",
|
||||
encoded: "DcuQKx8BES9wU7C6Q5VmLBjw436r27hayjS",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressScriptHash(
|
||||
[ripemd160.Size]byte{
|
||||
0xf0, 0xb4, 0xe8, 0x51, 0x00, 0xae, 0xe1, 0xa9, 0x96, 0xf2,
|
||||
0x29, 0x15, 0xeb, 0x3c, 0x3f, 0x76, 0x4d, 0x53, 0x77, 0x9a},
|
||||
chaincfg.MainNetParams.ScriptHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
txscript := []byte{
|
||||
0x51, 0x21, 0x03, 0xaa, 0x43, 0xf0, 0xa6, 0xc1, 0x57, 0x30,
|
||||
0xd8, 0x86, 0xcc, 0x1f, 0x03, 0x42, 0x04, 0x6d, 0x20, 0x17,
|
||||
0x54, 0x83, 0xd9, 0x0d, 0x7c, 0xcb, 0x65, 0x7f, 0x90, 0xc4,
|
||||
0x89, 0x11, 0x1d, 0x79, 0x4c, 0x51, 0xae}
|
||||
return dcrutil.NewAddressScriptHash(txscript, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
// Taken from transactions:
|
||||
// output: b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d
|
||||
// input: (not yet redeemed at time test was written)
|
||||
name: "mainnet p2sh 2",
|
||||
addr: "DcqgK4N4Ccucu2Sq4VDAdu4wH4LASLhzLVp",
|
||||
encoded: "DcqgK4N4Ccucu2Sq4VDAdu4wH4LASLhzLVp",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressScriptHash(
|
||||
[ripemd160.Size]byte{
|
||||
0xc7, 0xda, 0x50, 0x95, 0x68, 0x34, 0x36, 0xf4, 0x43, 0x5f,
|
||||
0xc4, 0xe7, 0x16, 0x3d, 0xca, 0xfd, 0xa1, 0xa2, 0xd0, 0x07},
|
||||
chaincfg.MainNetParams.ScriptHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
hash := []byte{
|
||||
0xc7, 0xda, 0x50, 0x95, 0x68, 0x34, 0x36, 0xf4, 0x43, 0x5f,
|
||||
0xc4, 0xe7, 0x16, 0x3d, 0xca, 0xfd, 0xa1, 0xa2, 0xd0, 0x07}
|
||||
return dcrutil.NewAddressScriptHashFromHash(hash, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
// Taken from bitcoind base58_keys_valid.
|
||||
name: "testnet p2sh",
|
||||
addr: "TccWLgcquqvwrfBocq5mcK5kBiyw8MvyvCi",
|
||||
encoded: "TccWLgcquqvwrfBocq5mcK5kBiyw8MvyvCi",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressScriptHash(
|
||||
[ripemd160.Size]byte{
|
||||
0x36, 0xc1, 0xca, 0x10, 0xa8, 0xa6, 0xa4, 0xb5, 0xd4, 0x20,
|
||||
0x4a, 0xc9, 0x70, 0x85, 0x39, 0x79, 0x90, 0x3a, 0xa2, 0x84},
|
||||
chaincfg.TestNet2Params.ScriptHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
hash := []byte{
|
||||
0x36, 0xc1, 0xca, 0x10, 0xa8, 0xa6, 0xa4, 0xb5, 0xd4, 0x20,
|
||||
0x4a, 0xc9, 0x70, 0x85, 0x39, 0x79, 0x90, 0x3a, 0xa2, 0x84}
|
||||
return dcrutil.NewAddressScriptHashFromHash(hash, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
|
||||
// Negative P2SH tests.
|
||||
{
|
||||
name: "p2sh wrong hash length",
|
||||
addr: "",
|
||||
valid: false,
|
||||
f: func() (dcrutil.Address, error) {
|
||||
hash := []byte{
|
||||
0x00, 0xf8, 0x15, 0xb0, 0x36, 0xd9, 0xbb, 0xbc, 0xe5, 0xe9,
|
||||
0xf2, 0xa0, 0x0a, 0xbd, 0x1b, 0xf3, 0xdc, 0x91, 0xe9, 0x55,
|
||||
0x10}
|
||||
return dcrutil.NewAddressScriptHashFromHash(hash, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
|
||||
// Positive P2PK tests.
|
||||
{
|
||||
name: "mainnet p2pk compressed (0x02)",
|
||||
addr: "DsT4FDqBKYG1Xr8aGrT1rKP3kiv6TZ5K5th",
|
||||
encoded: "DsT4FDqBKYG1Xr8aGrT1rKP3kiv6TZ5K5th",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x02, 0x8f, 0x53, 0x83, 0x8b, 0x76, 0x39, 0x56, 0x3f, 0x27,
|
||||
0xc9, 0x48, 0x45, 0x54, 0x9a, 0x41, 0xe5, 0x14, 0x6b, 0xcd,
|
||||
0x52, 0xe7, 0xfe, 0xf0, 0xea, 0x6d, 0xa1, 0x43, 0xa0, 0x2b,
|
||||
0x0f, 0xe2, 0xed},
|
||||
dcrutil.PKFCompressed, chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x02, 0x8f, 0x53, 0x83, 0x8b, 0x76, 0x39, 0x56, 0x3f, 0x27,
|
||||
0xc9, 0x48, 0x45, 0x54, 0x9a, 0x41, 0xe5, 0x14, 0x6b, 0xcd,
|
||||
0x52, 0xe7, 0xfe, 0xf0, 0xea, 0x6d, 0xa1, 0x43, 0xa0, 0x2b,
|
||||
0x0f, 0xe2, 0xed}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "mainnet p2pk compressed (0x03)",
|
||||
addr: "DsfiE2y23CGwKNxSGjbfPGeEW4xw1tamZdc",
|
||||
encoded: "DsfiE2y23CGwKNxSGjbfPGeEW4xw1tamZdc",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x03, 0xe9, 0x25, 0xaa, 0xfc, 0x1e, 0xdd, 0x44, 0xe7, 0xc7,
|
||||
0xf1, 0xea, 0x4f, 0xb7, 0xd2, 0x65, 0xdc, 0x67, 0x2f, 0x20,
|
||||
0x4c, 0x3d, 0x0c, 0x81, 0x93, 0x03, 0x89, 0xc1, 0x0b, 0x81,
|
||||
0xfb, 0x75, 0xde},
|
||||
dcrutil.PKFCompressed, chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x03, 0xe9, 0x25, 0xaa, 0xfc, 0x1e, 0xdd, 0x44, 0xe7, 0xc7,
|
||||
0xf1, 0xea, 0x4f, 0xb7, 0xd2, 0x65, 0xdc, 0x67, 0x2f, 0x20,
|
||||
0x4c, 0x3d, 0x0c, 0x81, 0x93, 0x03, 0x89, 0xc1, 0x0b, 0x81,
|
||||
0xfb, 0x75, 0xde}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
// Hybrid, uncompressed and compressed key types are supported, dcrd consensus rules require a compressed key type however.
|
||||
{
|
||||
name: "mainnet p2pk uncompressed (0x04)",
|
||||
addr: "DkM3EyZ546GghVSkvzb6J47PvGDyntqiDtFgipQhNj78Xm2mUYRpf",
|
||||
encoded: "DsfFjaADsV8c5oHWx85ZqfxCZy74K8RFuhK",
|
||||
valid: true,
|
||||
saddr: "0264c44653d6567eff5753c5d24a682ddc2b2cadfe1b0c6433b16374dace6778f0",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x04, 0x64, 0xc4, 0x46, 0x53, 0xd6, 0x56, 0x7e, 0xff, 0x57,
|
||||
0x53, 0xc5, 0xd2, 0x4a, 0x68, 0x2d, 0xdc, 0x2b, 0x2c, 0xad,
|
||||
0xfe, 0x1b, 0x0c, 0x64, 0x33, 0xb1, 0x63, 0x74, 0xda, 0xce,
|
||||
0x67, 0x78, 0xf0, 0xb8, 0x7c, 0xa4, 0x27, 0x9b, 0x56, 0x5d,
|
||||
0x21, 0x30, 0xce, 0x59, 0xf7, 0x5b, 0xfb, 0xb2, 0xb8, 0x8d,
|
||||
0xa7, 0x94, 0x14, 0x3d, 0x7c, 0xfd, 0x3e, 0x80, 0x80, 0x8a,
|
||||
0x1f, 0xa3, 0x20, 0x39, 0x04},
|
||||
dcrutil.PKFUncompressed, chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x04, 0x64, 0xc4, 0x46, 0x53, 0xd6, 0x56, 0x7e, 0xff, 0x57,
|
||||
0x53, 0xc5, 0xd2, 0x4a, 0x68, 0x2d, 0xdc, 0x2b, 0x2c, 0xad,
|
||||
0xfe, 0x1b, 0x0c, 0x64, 0x33, 0xb1, 0x63, 0x74, 0xda, 0xce,
|
||||
0x67, 0x78, 0xf0, 0xb8, 0x7c, 0xa4, 0x27, 0x9b, 0x56, 0x5d,
|
||||
0x21, 0x30, 0xce, 0x59, 0xf7, 0x5b, 0xfb, 0xb2, 0xb8, 0x8d,
|
||||
0xa7, 0x94, 0x14, 0x3d, 0x7c, 0xfd, 0x3e, 0x80, 0x80, 0x8a,
|
||||
0x1f, 0xa3, 0x20, 0x39, 0x04}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "mainnet p2pk hybrid (0x06)",
|
||||
addr: "DkM3EyZ546GghVSkvzb6J47PvGDyntqiDtFgipQhNj78Xm2mUYRpf",
|
||||
encoded: "DsfFjaADsV8c5oHWx85ZqfxCZy74K8RFuhK",
|
||||
valid: true,
|
||||
saddr: "0264c44653d6567eff5753c5d24a682ddc2b2cadfe1b0c6433b16374dace6778f0",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x06, 0x64, 0xc4, 0x46, 0x53, 0xd6, 0x56, 0x7e, 0xff, 0x57,
|
||||
0x53, 0xc5, 0xd2, 0x4a, 0x68, 0x2d, 0xdc, 0x2b, 0x2c, 0xad,
|
||||
0xfe, 0x1b, 0x0c, 0x64, 0x33, 0xb1, 0x63, 0x74, 0xda, 0xce,
|
||||
0x67, 0x78, 0xf0, 0xb8, 0x7c, 0xa4, 0x27, 0x9b, 0x56, 0x5d,
|
||||
0x21, 0x30, 0xce, 0x59, 0xf7, 0x5b, 0xfb, 0xb2, 0xb8, 0x8d,
|
||||
0xa7, 0x94, 0x14, 0x3d, 0x7c, 0xfd, 0x3e, 0x80, 0x80, 0x8a,
|
||||
0x1f, 0xa3, 0x20, 0x39, 0x04},
|
||||
dcrutil.PKFHybrid, chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x06, 0x64, 0xc4, 0x46, 0x53, 0xd6, 0x56, 0x7e, 0xff, 0x57,
|
||||
0x53, 0xc5, 0xd2, 0x4a, 0x68, 0x2d, 0xdc, 0x2b, 0x2c, 0xad,
|
||||
0xfe, 0x1b, 0x0c, 0x64, 0x33, 0xb1, 0x63, 0x74, 0xda, 0xce,
|
||||
0x67, 0x78, 0xf0, 0xb8, 0x7c, 0xa4, 0x27, 0x9b, 0x56, 0x5d,
|
||||
0x21, 0x30, 0xce, 0x59, 0xf7, 0x5b, 0xfb, 0xb2, 0xb8, 0x8d,
|
||||
0xa7, 0x94, 0x14, 0x3d, 0x7c, 0xfd, 0x3e, 0x80, 0x80, 0x8a,
|
||||
0x1f, 0xa3, 0x20, 0x39, 0x04}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "mainnet p2pk hybrid (0x07)",
|
||||
addr: "DkRKh2aTdwjKKL1mkCb2DFp2Hr7SqMyx3zWqNwyc37PYiGpKmGRsi",
|
||||
encoded: "DskEQZMCs4nifL7wx7iHYGWxMQvR9ThCBKQ",
|
||||
valid: true,
|
||||
saddr: "03348d8aeb4253ca52456fe5da94ab1263bfee16bb8192497f666389ca964f8479",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x07, 0x34, 0x8d, 0x8a, 0xeb, 0x42, 0x53, 0xca, 0x52, 0x45,
|
||||
0x6f, 0xe5, 0xda, 0x94, 0xab, 0x12, 0x63, 0xbf, 0xee, 0x16,
|
||||
0xbb, 0x81, 0x92, 0x49, 0x7f, 0x66, 0x63, 0x89, 0xca, 0x96,
|
||||
0x4f, 0x84, 0x79, 0x83, 0x75, 0x12, 0x9d, 0x79, 0x58, 0x84,
|
||||
0x3b, 0x14, 0x25, 0x8b, 0x90, 0x5d, 0xc9, 0x4f, 0xae, 0xd3,
|
||||
0x24, 0xdd, 0x8a, 0x9d, 0x67, 0xff, 0xac, 0x8c, 0xc0, 0xa8,
|
||||
0x5b, 0xe8, 0x4b, 0xac, 0x5d},
|
||||
dcrutil.PKFHybrid, chaincfg.MainNetParams.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x07, 0x34, 0x8d, 0x8a, 0xeb, 0x42, 0x53, 0xca, 0x52, 0x45,
|
||||
0x6f, 0xe5, 0xda, 0x94, 0xab, 0x12, 0x63, 0xbf, 0xee, 0x16,
|
||||
0xbb, 0x81, 0x92, 0x49, 0x7f, 0x66, 0x63, 0x89, 0xca, 0x96,
|
||||
0x4f, 0x84, 0x79, 0x83, 0x75, 0x12, 0x9d, 0x79, 0x58, 0x84,
|
||||
0x3b, 0x14, 0x25, 0x8b, 0x90, 0x5d, 0xc9, 0x4f, 0xae, 0xd3,
|
||||
0x24, 0xdd, 0x8a, 0x9d, 0x67, 0xff, 0xac, 0x8c, 0xc0, 0xa8,
|
||||
0x5b, 0xe8, 0x4b, 0xac, 0x5d}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.MainNetParams)
|
||||
},
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pk compressed (0x02)",
|
||||
addr: "Tso9sQD3ALqRsmEkAm7KvPrkGbeG2Vun7Kv",
|
||||
encoded: "Tso9sQD3ALqRsmEkAm7KvPrkGbeG2Vun7Kv",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x02, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e},
|
||||
dcrutil.PKFCompressed, chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x02, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pk compressed (0x03)",
|
||||
addr: "TsWZ1EzypJfMwBKAEDYKuyHRGctqGAxMje2",
|
||||
encoded: "TsWZ1EzypJfMwBKAEDYKuyHRGctqGAxMje2",
|
||||
valid: true,
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x03, 0x08, 0x44, 0xee, 0x70, 0xd8, 0x38, 0x4d, 0x52, 0x50,
|
||||
0xe9, 0xbb, 0x3a, 0x6a, 0x73, 0xd4, 0xb5, 0xbe, 0xc7, 0x70,
|
||||
0xe8, 0xb3, 0x1d, 0x6a, 0x0a, 0xe9, 0xfb, 0x73, 0x90, 0x09,
|
||||
0xd9, 0x1a, 0xf5},
|
||||
dcrutil.PKFCompressed, chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x03, 0x08, 0x44, 0xee, 0x70, 0xd8, 0x38, 0x4d, 0x52, 0x50,
|
||||
0xe9, 0xbb, 0x3a, 0x6a, 0x73, 0xd4, 0xb5, 0xbe, 0xc7, 0x70,
|
||||
0xe8, 0xb3, 0x1d, 0x6a, 0x0a, 0xe9, 0xfb, 0x73, 0x90, 0x09,
|
||||
0xd9, 0x1a, 0xf5}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pk uncompressed (0x04)",
|
||||
addr: "TkKmMiY5iDh4U3KkSopYgkU1AzhAcQZiSoVhYhFymZHGMi9LM9Fdt",
|
||||
encoded: "Tso9sQD3ALqRsmEkAm7KvPrkGbeG2Vun7Kv",
|
||||
valid: true,
|
||||
saddr: "026a40c403e74670c4de7656a09caa2353d4b383a9ce66eef51e1220eacf4be06e",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x04, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e, 0xd5, 0x48, 0xc8, 0xc1, 0x6f, 0xb5, 0xeb,
|
||||
0x90, 0x07, 0xcb, 0x94, 0x22, 0x0b, 0x3b, 0xb8, 0x94, 0x91,
|
||||
0xd5, 0xa1, 0xfd, 0x2d, 0x77, 0x86, 0x7f, 0xca, 0x64, 0x21,
|
||||
0x7a, 0xce, 0xcf, 0x22, 0x44},
|
||||
dcrutil.PKFUncompressed, chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x04, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e, 0xd5, 0x48, 0xc8, 0xc1, 0x6f, 0xb5, 0xeb,
|
||||
0x90, 0x07, 0xcb, 0x94, 0x22, 0x0b, 0x3b, 0xb8, 0x94, 0x91,
|
||||
0xd5, 0xa1, 0xfd, 0x2d, 0x77, 0x86, 0x7f, 0xca, 0x64, 0x21,
|
||||
0x7a, 0xce, 0xcf, 0x22, 0x44}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pk hybrid (0x06)",
|
||||
addr: "TkKmMiY5iDh4U3KkSopYgkU1AzhAcQZiSoVhYhFymZHGMi9LM9Fdt",
|
||||
encoded: "Tso9sQD3ALqRsmEkAm7KvPrkGbeG2Vun7Kv",
|
||||
valid: true,
|
||||
saddr: "026a40c403e74670c4de7656a09caa2353d4b383a9ce66eef51e1220eacf4be06e",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x06, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e, 0xd5, 0x48, 0xc8, 0xc1, 0x6f, 0xb5, 0xeb,
|
||||
0x90, 0x07, 0xcb, 0x94, 0x22, 0x0b, 0x3b, 0xb8, 0x94, 0x91,
|
||||
0xd5, 0xa1, 0xfd, 0x2d, 0x77, 0x86, 0x7f, 0xca, 0x64, 0x21,
|
||||
0x7a, 0xce, 0xcf, 0x22, 0x44},
|
||||
dcrutil.PKFHybrid, chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x06, 0x6a, 0x40, 0xc4, 0x03, 0xe7, 0x46, 0x70, 0xc4, 0xde,
|
||||
0x76, 0x56, 0xa0, 0x9c, 0xaa, 0x23, 0x53, 0xd4, 0xb3, 0x83,
|
||||
0xa9, 0xce, 0x66, 0xee, 0xf5, 0x1e, 0x12, 0x20, 0xea, 0xcf,
|
||||
0x4b, 0xe0, 0x6e, 0xd5, 0x48, 0xc8, 0xc1, 0x6f, 0xb5, 0xeb,
|
||||
0x90, 0x07, 0xcb, 0x94, 0x22, 0x0b, 0x3b, 0xb8, 0x94, 0x91,
|
||||
0xd5, 0xa1, 0xfd, 0x2d, 0x77, 0x86, 0x7f, 0xca, 0x64, 0x21,
|
||||
0x7a, 0xce, 0xcf, 0x22, 0x44}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "testnet p2pk hybrid (0x07)",
|
||||
addr: "TkQ5Ax2ieEZpBDA963VDH4y27KMpXtP8qyeykzwBNFocDc8ZKqTGz",
|
||||
encoded: "TsTFLdM32YVrYsEQDFxo2zmPuKFcFhH5ZT3",
|
||||
valid: true,
|
||||
saddr: "03edd40747de905a9becb14987a1a26c1adbd617c45e1583c142a635bfda9493df",
|
||||
result: dcrutil.TstAddressPubKey(
|
||||
[]byte{
|
||||
0x07, 0xed, 0xd4, 0x07, 0x47, 0xde, 0x90, 0x5a, 0x9b, 0xec,
|
||||
0xb1, 0x49, 0x87, 0xa1, 0xa2, 0x6c, 0x1a, 0xdb, 0xd6, 0x17,
|
||||
0xc4, 0x5e, 0x15, 0x83, 0xc1, 0x42, 0xa6, 0x35, 0xbf, 0xda,
|
||||
0x94, 0x93, 0xdf, 0xa1, 0xc6, 0xd3, 0x67, 0x35, 0x97, 0x49,
|
||||
0x65, 0xfe, 0x7b, 0x86, 0x1e, 0x7f, 0x6f, 0xcc, 0x08, 0x7d,
|
||||
0xc7, 0xfe, 0x47, 0x38, 0x0f, 0xa8, 0xbd, 0xe0, 0xd9, 0xc3,
|
||||
0x22, 0xd5, 0x3c, 0x0e, 0x89},
|
||||
dcrutil.PKFHybrid, chaincfg.TestNet2Params.PubKeyHashAddrID),
|
||||
f: func() (dcrutil.Address, error) {
|
||||
serializedPubKey := []byte{
|
||||
0x07, 0xed, 0xd4, 0x07, 0x47, 0xde, 0x90, 0x5a, 0x9b, 0xec,
|
||||
0xb1, 0x49, 0x87, 0xa1, 0xa2, 0x6c, 0x1a, 0xdb, 0xd6, 0x17,
|
||||
0xc4, 0x5e, 0x15, 0x83, 0xc1, 0x42, 0xa6, 0x35, 0xbf, 0xda,
|
||||
0x94, 0x93, 0xdf, 0xa1, 0xc6, 0xd3, 0x67, 0x35, 0x97, 0x49,
|
||||
0x65, 0xfe, 0x7b, 0x86, 0x1e, 0x7f, 0x6f, 0xcc, 0x08, 0x7d,
|
||||
0xc7, 0xfe, 0x47, 0x38, 0x0f, 0xa8, 0xbd, 0xe0, 0xd9, 0xc3,
|
||||
0x22, 0xd5, 0x3c, 0x0e, 0x89}
|
||||
return dcrutil.NewAddressSecpPubKey(serializedPubKey, &chaincfg.TestNet2Params)
|
||||
},
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// Decode addr and compare error against valid.
|
||||
decoded, err := dcrutil.DecodeAddress(test.addr)
|
||||
if (err == nil) != test.valid {
|
||||
t.Errorf("%v: decoding test failed: %v", test.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Ensure the stringer returns the same address as the
|
||||
// original.
|
||||
if decodedStringer, ok := decoded.(fmt.Stringer); ok {
|
||||
if test.addr != decodedStringer.String() {
|
||||
t.Errorf("%v: String on decoded value does not match expected value: %v != %v",
|
||||
test.name, test.addr, decodedStringer.String())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Encode again and compare against the original.
|
||||
encoded := decoded.EncodeAddress()
|
||||
if test.encoded != encoded {
|
||||
t.Errorf("%v: decoding and encoding produced different addressess: %v != %v",
|
||||
test.name, test.encoded, encoded)
|
||||
return
|
||||
}
|
||||
|
||||
// Perform type-specific calculations.
|
||||
var saddr []byte
|
||||
switch d := decoded.(type) {
|
||||
case *dcrutil.AddressPubKeyHash:
|
||||
saddr = dcrutil.TstAddressSAddr(encoded)
|
||||
|
||||
case *dcrutil.AddressScriptHash:
|
||||
saddr = dcrutil.TstAddressSAddr(encoded)
|
||||
|
||||
case *dcrutil.AddressSecpPubKey:
|
||||
// Ignore the error here since the script
|
||||
// address is checked below.
|
||||
saddr, err = hex.DecodeString(d.String())
|
||||
if err != nil {
|
||||
saddr, _ = hex.DecodeString(test.saddr)
|
||||
}
|
||||
|
||||
case *dcrutil.AddressEdwardsPubKey:
|
||||
// Ignore the error here since the script
|
||||
// address is checked below.
|
||||
saddr, _ = hex.DecodeString(d.String())
|
||||
|
||||
case *dcrutil.AddressSecSchnorrPubKey:
|
||||
// Ignore the error here since the script
|
||||
// address is checked below.
|
||||
saddr, _ = hex.DecodeString(d.String())
|
||||
}
|
||||
|
||||
// Check script address, as well as the Hash160 method for P2PKH and
|
||||
// P2SH addresses.
|
||||
if !bytes.Equal(saddr, decoded.ScriptAddress()) {
|
||||
t.Errorf("%v: script addresses do not match:\n%x != \n%x",
|
||||
test.name, saddr, decoded.ScriptAddress())
|
||||
return
|
||||
}
|
||||
switch a := decoded.(type) {
|
||||
case *dcrutil.AddressPubKeyHash:
|
||||
if h := a.Hash160()[:]; !bytes.Equal(saddr, h) {
|
||||
t.Errorf("%v: hashes do not match:\n%x != \n%x",
|
||||
test.name, saddr, h)
|
||||
return
|
||||
}
|
||||
|
||||
case *dcrutil.AddressScriptHash:
|
||||
if h := a.Hash160()[:]; !bytes.Equal(saddr, h) {
|
||||
t.Errorf("%v: hashes do not match:\n%x != \n%x",
|
||||
test.name, saddr, h)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the address is for the expected network.
|
||||
if !decoded.IsForNet(test.net) {
|
||||
t.Errorf("%v: calculated network does not match expected",
|
||||
test.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !test.valid {
|
||||
// If address is invalid, but a creation function exists,
|
||||
// verify that it returns a nil addr and non-nil error.
|
||||
if test.f != nil {
|
||||
_, err := test.f()
|
||||
if err == nil {
|
||||
t.Errorf("%v: address is invalid but creating new address succeeded",
|
||||
test.name)
|
||||
return
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Valid test, compare address created with f against expected result.
|
||||
addr, err := test.f()
|
||||
if err != nil {
|
||||
t.Errorf("%v: address is valid but creating new address failed with error %v",
|
||||
test.name, err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(addr.ScriptAddress(), test.result.ScriptAddress()) {
|
||||
t.Errorf("%v: created address does not match expected result \n "+
|
||||
" got %x, expected %x",
|
||||
test.name, addr.ScriptAddress(), test.result.ScriptAddress())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
146
dcrutil/amount.go
Normal file
146
dcrutil/amount.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// AmountUnit describes a method of converting an Amount to something
|
||||
// other than the base unit of a coin. The value of the AmountUnit
|
||||
// is the exponent component of the decadic multiple to convert from
|
||||
// an amount in coins to an amount counted in atomic units.
|
||||
type AmountUnit int
|
||||
|
||||
// These constants define various units used when describing a coin
|
||||
// monetary amount.
|
||||
const (
|
||||
AmountMegaCoin AmountUnit = 6
|
||||
AmountKiloCoin AmountUnit = 3
|
||||
AmountCoin AmountUnit = 0
|
||||
AmountMilliCoin AmountUnit = -3
|
||||
AmountMicroCoin AmountUnit = -6
|
||||
AmountAtom AmountUnit = -8
|
||||
)
|
||||
|
||||
// String returns the unit as a string. For recognized units, the SI
|
||||
// prefix is used, or "Atom" for the base unit. For all unrecognized
|
||||
// units, "1eN DCR" is returned, where N is the AmountUnit.
|
||||
func (u AmountUnit) String() string {
|
||||
switch u {
|
||||
case AmountMegaCoin:
|
||||
return "MDCR"
|
||||
case AmountKiloCoin:
|
||||
return "kDCR"
|
||||
case AmountCoin:
|
||||
return "DCR"
|
||||
case AmountMilliCoin:
|
||||
return "mDCR"
|
||||
case AmountMicroCoin:
|
||||
return "μDCR"
|
||||
case AmountAtom:
|
||||
return "Atom"
|
||||
default:
|
||||
return "1e" + strconv.FormatInt(int64(u), 10) + " DCR"
|
||||
}
|
||||
}
|
||||
|
||||
// Amount represents the base coin monetary unit (colloquially referred
|
||||
// to as an `Atom'). A single Amount is equal to 1e-8 of a coin.
|
||||
type Amount int64
|
||||
|
||||
// round converts a floating point number, which may or may not be representable
|
||||
// as an integer, to the Amount integer type by rounding to the nearest integer.
|
||||
// This is performed by adding or subtracting 0.5 depending on the sign, and
|
||||
// relying on integer truncation to round the value to the nearest Amount.
|
||||
func round(f float64) Amount {
|
||||
if f < 0 {
|
||||
return Amount(f - 0.5)
|
||||
}
|
||||
return Amount(f + 0.5)
|
||||
}
|
||||
|
||||
// NewAmount creates an Amount from a floating point value representing
|
||||
// some value in the currency. NewAmount errors if f is NaN or +-Infinity,
|
||||
// but does not check that the amount is within the total amount of coins
|
||||
// producible as f may not refer to an amount at a single moment in time.
|
||||
//
|
||||
// NewAmount is for specifically for converting DCR to Atoms (atomic units).
|
||||
// For creating a new Amount with an int64 value which denotes a quantity of
|
||||
// Atoms, do a simple type conversion from type int64 to Amount.
|
||||
// See GoDoc for example: http://godoc.org/github.com/decred/dcrd/dcrutil#example-Amount
|
||||
func NewAmount(f float64) (Amount, error) {
|
||||
// The amount is only considered invalid if it cannot be represented
|
||||
// as an integer type. This may happen if f is NaN or +-Infinity.
|
||||
switch {
|
||||
case math.IsNaN(f):
|
||||
fallthrough
|
||||
case math.IsInf(f, 1):
|
||||
fallthrough
|
||||
case math.IsInf(f, -1):
|
||||
return 0, errors.New("invalid coin amount")
|
||||
}
|
||||
|
||||
return round(f * AtomsPerCoin), nil
|
||||
}
|
||||
|
||||
// ToUnit converts a monetary amount counted in coin base units to a
|
||||
// floating point value representing an amount of coins.
|
||||
func (a Amount) ToUnit(u AmountUnit) float64 {
|
||||
return float64(a) / math.Pow10(int(u+8))
|
||||
}
|
||||
|
||||
// ToCoin is the equivalent of calling ToUnit with AmountCoin.
|
||||
func (a Amount) ToCoin() float64 {
|
||||
return a.ToUnit(AmountCoin)
|
||||
}
|
||||
|
||||
// Format formats a monetary amount counted in coin base units as a
|
||||
// string for a given unit. The conversion will succeed for any unit,
|
||||
// however, known units will be formated with an appended label describing
|
||||
// the units with SI notation, or "atom" for the base unit.
|
||||
func (a Amount) Format(u AmountUnit) string {
|
||||
units := " " + u.String()
|
||||
return strconv.FormatFloat(a.ToUnit(u), 'f', -int(u+8), 64) + units
|
||||
}
|
||||
|
||||
// String is the equivalent of calling Format with AmountCoin.
|
||||
func (a Amount) String() string {
|
||||
return a.Format(AmountCoin)
|
||||
}
|
||||
|
||||
// MulF64 multiplies an Amount by a floating point value. While this is not
|
||||
// an operation that must typically be done by a full node or wallet, it is
|
||||
// useful for services that build on top of decred (for example, calculating
|
||||
// a fee by multiplying by a percentage).
|
||||
func (a Amount) MulF64(f float64) Amount {
|
||||
return round(float64(a) * f)
|
||||
}
|
||||
|
||||
// AmountSorter implements sort.Interface to allow a slice of Amounts to
|
||||
// be sorted.
|
||||
type AmountSorter []Amount
|
||||
|
||||
// Len returns the number of Amounts in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s AmountSorter) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps the Amounts at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s AmountSorter) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less returns whether the Amount with index i should sort before the
|
||||
// Amount with index j. It is part of the sort.Interface
|
||||
// implementation.
|
||||
func (s AmountSorter) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
||||
352
dcrutil/amount_test.go
Normal file
352
dcrutil/amount_test.go
Normal file
@ -0,0 +1,352 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
. "github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
func TestAmountCreation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
amount float64
|
||||
valid bool
|
||||
expected Amount
|
||||
}{
|
||||
// Positive tests.
|
||||
{
|
||||
name: "zero",
|
||||
amount: 0,
|
||||
valid: true,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "max producable",
|
||||
amount: 21e6,
|
||||
valid: true,
|
||||
expected: MaxAmount,
|
||||
},
|
||||
{
|
||||
name: "min producable",
|
||||
amount: -21e6,
|
||||
valid: true,
|
||||
expected: -MaxAmount,
|
||||
},
|
||||
{
|
||||
name: "exceeds max producable",
|
||||
amount: 21e6 + 1e-8,
|
||||
valid: true,
|
||||
expected: MaxAmount + 1,
|
||||
},
|
||||
{
|
||||
name: "exceeds min producable",
|
||||
amount: -21e6 - 1e-8,
|
||||
valid: true,
|
||||
expected: -MaxAmount - 1,
|
||||
},
|
||||
{
|
||||
name: "one hundred",
|
||||
amount: 100,
|
||||
valid: true,
|
||||
expected: 100 * AtomsPerCoin,
|
||||
},
|
||||
{
|
||||
name: "fraction",
|
||||
amount: 0.01234567,
|
||||
valid: true,
|
||||
expected: 1234567,
|
||||
},
|
||||
{
|
||||
name: "rounding up",
|
||||
amount: 54.999999999999943157,
|
||||
valid: true,
|
||||
expected: 55 * AtomsPerCoin,
|
||||
},
|
||||
{
|
||||
name: "rounding down",
|
||||
amount: 55.000000000000056843,
|
||||
valid: true,
|
||||
expected: 55 * AtomsPerCoin,
|
||||
},
|
||||
|
||||
// Negative tests.
|
||||
{
|
||||
name: "not-a-number",
|
||||
amount: math.NaN(),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "-infinity",
|
||||
amount: math.Inf(-1),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
name: "+infinity",
|
||||
amount: math.Inf(1),
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
a, err := NewAmount(test.amount)
|
||||
switch {
|
||||
case test.valid && err != nil:
|
||||
t.Errorf("%v: Positive test Amount creation failed with: %v", test.name, err)
|
||||
continue
|
||||
case !test.valid && err == nil:
|
||||
t.Errorf("%v: Negative test Amount creation succeeded (value %v) when should fail", test.name, a)
|
||||
continue
|
||||
}
|
||||
|
||||
if a != test.expected {
|
||||
t.Errorf("%v: Created amount %v does not match expected %v", test.name, a, test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAmountUnitConversions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
amount Amount
|
||||
unit AmountUnit
|
||||
converted float64
|
||||
s string
|
||||
}{
|
||||
{
|
||||
name: "MDCR",
|
||||
amount: MaxAmount,
|
||||
unit: AmountMegaCoin,
|
||||
converted: 21,
|
||||
s: "21 MDCR",
|
||||
},
|
||||
{
|
||||
name: "kDCR",
|
||||
amount: 44433322211100,
|
||||
unit: AmountKiloCoin,
|
||||
converted: 444.33322211100,
|
||||
s: "444.333222111 kDCR",
|
||||
},
|
||||
{
|
||||
name: "Coin",
|
||||
amount: 44433322211100,
|
||||
unit: AmountCoin,
|
||||
converted: 444333.22211100,
|
||||
s: "444333.222111 DCR",
|
||||
},
|
||||
{
|
||||
name: "mDCR",
|
||||
amount: 44433322211100,
|
||||
unit: AmountMilliCoin,
|
||||
converted: 444333222.11100,
|
||||
s: "444333222.111 mDCR",
|
||||
},
|
||||
{
|
||||
|
||||
name: "μDCR",
|
||||
amount: 44433322211100,
|
||||
unit: AmountMicroCoin,
|
||||
converted: 444333222111.00,
|
||||
s: "444333222111 μDCR",
|
||||
},
|
||||
{
|
||||
|
||||
name: "atom",
|
||||
amount: 44433322211100,
|
||||
unit: AmountAtom,
|
||||
converted: 44433322211100,
|
||||
s: "44433322211100 Atom",
|
||||
},
|
||||
{
|
||||
|
||||
name: "non-standard unit",
|
||||
amount: 44433322211100,
|
||||
unit: AmountUnit(-1),
|
||||
converted: 4443332.2211100,
|
||||
s: "4443332.22111 1e-1 DCR",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
f := test.amount.ToUnit(test.unit)
|
||||
if f != test.converted {
|
||||
t.Errorf("%v: converted value %v does not match expected %v", test.name, f, test.converted)
|
||||
continue
|
||||
}
|
||||
|
||||
s := test.amount.Format(test.unit)
|
||||
if s != test.s {
|
||||
t.Errorf("%v: format '%v' does not match expected '%v'", test.name, s, test.s)
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify that Amount.ToCoin works as advertised.
|
||||
f1 := test.amount.ToUnit(AmountCoin)
|
||||
f2 := test.amount.ToCoin()
|
||||
if f1 != f2 {
|
||||
t.Errorf("%v: ToCoin does not match ToUnit(AmountCoin): %v != %v", test.name, f1, f2)
|
||||
}
|
||||
|
||||
// Verify that Amount.String works as advertised.
|
||||
s1 := test.amount.Format(AmountCoin)
|
||||
s2 := test.amount.String()
|
||||
if s1 != s2 {
|
||||
t.Errorf("%v: String does not match Format(AmountCoin): %v != %v", test.name, s1, s2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAmountMulF64(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
amt Amount
|
||||
mul float64
|
||||
res Amount
|
||||
}{
|
||||
{
|
||||
name: "Multiply 0.1 DCR by 2",
|
||||
amt: 100e5, // 0.1 DCR
|
||||
mul: 2,
|
||||
res: 200e5, // 0.2 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply 0.2 DCR by 0.02",
|
||||
amt: 200e5, // 0.2 DCR
|
||||
mul: 1.02,
|
||||
res: 204e5, // 0.204 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply 0.1 DCR by -2",
|
||||
amt: 100e5, // 0.1 DCR
|
||||
mul: -2,
|
||||
res: -200e5, // -0.2 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply 0.2 DCR by -0.02",
|
||||
amt: 200e5, // 0.2 DCR
|
||||
mul: -1.02,
|
||||
res: -204e5, // -0.204 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply -0.1 DCR by 2",
|
||||
amt: -100e5, // -0.1 DCR
|
||||
mul: 2,
|
||||
res: -200e5, // -0.2 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply -0.2 DCR by 0.02",
|
||||
amt: -200e5, // -0.2 DCR
|
||||
mul: 1.02,
|
||||
res: -204e5, // -0.204 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply -0.1 DCR by -2",
|
||||
amt: -100e5, // -0.1 DCR
|
||||
mul: -2,
|
||||
res: 200e5, // 0.2 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply -0.2 DCR by -0.02",
|
||||
amt: -200e5, // -0.2 DCR
|
||||
mul: -1.02,
|
||||
res: 204e5, // 0.204 DCR
|
||||
},
|
||||
{
|
||||
name: "Round down",
|
||||
amt: 49, // 49 Atoms
|
||||
mul: 0.01,
|
||||
res: 0,
|
||||
},
|
||||
{
|
||||
name: "Round up",
|
||||
amt: 50, // 50 Atoms
|
||||
mul: 0.01,
|
||||
res: 1, // 1 Atom
|
||||
},
|
||||
{
|
||||
name: "Multiply by 0.",
|
||||
amt: 1e8, // 1 DCR
|
||||
mul: 0,
|
||||
res: 0, // 0 DCR
|
||||
},
|
||||
{
|
||||
name: "Multiply 1 by 0.5.",
|
||||
amt: 1, // 1 Atom
|
||||
mul: 0.5,
|
||||
res: 1, // 1 Atom
|
||||
},
|
||||
{
|
||||
name: "Multiply 100 by 66%.",
|
||||
amt: 100, // 100 Atoms
|
||||
mul: 0.66,
|
||||
res: 66, // 66 Atoms
|
||||
},
|
||||
{
|
||||
name: "Multiply 100 by 66.6%.",
|
||||
amt: 100, // 100 Atoms
|
||||
mul: 0.666,
|
||||
res: 67, // 67 Atoms
|
||||
},
|
||||
{
|
||||
name: "Multiply 100 by 2/3.",
|
||||
amt: 100, // 100 Atoms
|
||||
mul: 2.0 / 3,
|
||||
res: 67, // 67 Atoms
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
a := test.amt.MulF64(test.mul)
|
||||
if a != test.res {
|
||||
t.Errorf("%v: expected %v got %v", test.name, test.res, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAmountSorter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
as []Amount
|
||||
want []Amount
|
||||
}{
|
||||
{
|
||||
name: "Sort zero length slice of Amounts",
|
||||
as: []Amount{},
|
||||
want: []Amount{},
|
||||
},
|
||||
{
|
||||
name: "Sort 1-element slice of Amounts",
|
||||
as: []Amount{7},
|
||||
want: []Amount{7},
|
||||
},
|
||||
{
|
||||
name: "Sort 2-element slice of Amounts",
|
||||
as: []Amount{7, 5},
|
||||
want: []Amount{5, 7},
|
||||
},
|
||||
{
|
||||
name: "Sort 6-element slice of Amounts",
|
||||
as: []Amount{0, 9e8, 4e6, 4e6, 3, 9e12},
|
||||
want: []Amount{0, 3, 4e6, 4e6, 9e8, 9e12},
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
result := make([]Amount, len(test.as))
|
||||
copy(result, test.as)
|
||||
sort.Sort(AmountSorter(result))
|
||||
if !reflect.DeepEqual(result, test.want) {
|
||||
t.Errorf("AmountSorter #%d got %v want %v", i, result,
|
||||
test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
108
dcrutil/appdata.go
Normal file
108
dcrutil/appdata.go
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// appDataDir returns an operating system specific directory to be used for
|
||||
// storing application data for an application. See AppDataDir for more
|
||||
// details. This unexported version takes an operating system argument
|
||||
// primarily to enable the testing package to properly test the function by
|
||||
// forcing an operating system that is not the currently one.
|
||||
func appDataDir(goos, appName string, roaming bool) string {
|
||||
if appName == "" || appName == "." {
|
||||
return "."
|
||||
}
|
||||
|
||||
// The caller really shouldn't prepend the appName with a period, but
|
||||
// if they do, handle it gracefully by stripping it.
|
||||
if strings.HasPrefix(appName, ".") {
|
||||
appName = appName[1:]
|
||||
}
|
||||
appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:]
|
||||
appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:]
|
||||
|
||||
// Get the OS specific home directory via the Go standard lib.
|
||||
var homeDir string
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
homeDir = usr.HomeDir
|
||||
}
|
||||
|
||||
// Fall back to standard HOME environment variable that works
|
||||
// for most POSIX OSes if the directory from the Go standard
|
||||
// lib failed.
|
||||
if err != nil || homeDir == "" {
|
||||
homeDir = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
switch goos {
|
||||
// Attempt to use the LOCALAPPDATA or APPDATA environment variable on
|
||||
// Windows.
|
||||
case "windows":
|
||||
// Windows XP and before didn't have a LOCALAPPDATA, so fallback
|
||||
// to regular APPDATA when LOCALAPPDATA is not set.
|
||||
appData := os.Getenv("LOCALAPPDATA")
|
||||
if roaming || appData == "" {
|
||||
appData = os.Getenv("APPDATA")
|
||||
}
|
||||
|
||||
if appData != "" {
|
||||
return filepath.Join(appData, appNameUpper)
|
||||
}
|
||||
|
||||
case "darwin":
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, "Library",
|
||||
"Application Support", appNameUpper)
|
||||
}
|
||||
|
||||
case "plan9":
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, appNameLower)
|
||||
}
|
||||
|
||||
default:
|
||||
if homeDir != "" {
|
||||
return filepath.Join(homeDir, "."+appNameLower)
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to the current directory if all else fails.
|
||||
return "."
|
||||
}
|
||||
|
||||
// AppDataDir returns an operating system specific directory to be used for
|
||||
// storing application data for an application.
|
||||
//
|
||||
// The appName parameter is the name of the application the data directory is
|
||||
// being requested for. This function will prepend a period to the appName for
|
||||
// POSIX style operating systems since that is standard practice. An empty
|
||||
// appName or one with a single dot is treated as requesting the current
|
||||
// directory so only "." will be returned. Further, the first character
|
||||
// of appName will be made lowercase for POSIX style operating systems and
|
||||
// uppercase for Mac and Windows since that is standard practice.
|
||||
//
|
||||
// The roaming parameter only applies to Windows where it specifies the roaming
|
||||
// application data profile (%APPDATA%) should be used instead of the local one
|
||||
// (%LOCALAPPDATA%) that is used by default.
|
||||
//
|
||||
// Example results:
|
||||
// dir := AppDataDir("myapp", false)
|
||||
// POSIX (Linux/BSD): ~/.myapp
|
||||
// Mac OS: $HOME/Library/Application Support/Myapp
|
||||
// Windows: %LOCALAPPDATA%\Myapp
|
||||
// Plan 9: $home/myapp
|
||||
func AppDataDir(appName string, roaming bool) string {
|
||||
return appDataDir(runtime.GOOS, appName, roaming)
|
||||
}
|
||||
134
dcrutil/appdata_test.go
Normal file
134
dcrutil/appdata_test.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"unicode"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// TestAppDataDir tests the API for AppDataDir to ensure it gives expected
|
||||
// results for various operating systems.
|
||||
func TestAppDataDir(t *testing.T) {
|
||||
// App name plus upper and lowercase variants.
|
||||
appName := "myapp"
|
||||
appNameUpper := string(unicode.ToUpper(rune(appName[0]))) + appName[1:]
|
||||
appNameLower := string(unicode.ToLower(rune(appName[0]))) + appName[1:]
|
||||
|
||||
// When we're on Windows, set the expected local and roaming directories
|
||||
// per the environment vars. When we aren't on Windows, the function
|
||||
// should return the current directory when forced to provide the
|
||||
// Windows path since the environment variables won't exist.
|
||||
winLocal := "."
|
||||
winRoaming := "."
|
||||
if runtime.GOOS == "windows" {
|
||||
localAppData := os.Getenv("LOCALAPPDATA")
|
||||
roamingAppData := os.Getenv("APPDATA")
|
||||
if localAppData == "" {
|
||||
localAppData = roamingAppData
|
||||
}
|
||||
winLocal = filepath.Join(localAppData, appNameUpper)
|
||||
winRoaming = filepath.Join(roamingAppData, appNameUpper)
|
||||
}
|
||||
|
||||
// Get the home directory to use for testing expected results.
|
||||
var homeDir string
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
t.Errorf("user.Current: %v", err)
|
||||
return
|
||||
}
|
||||
homeDir = usr.HomeDir
|
||||
|
||||
// Mac app data directory.
|
||||
macAppData := filepath.Join(homeDir, "Library", "Application Support")
|
||||
|
||||
tests := []struct {
|
||||
goos string
|
||||
appName string
|
||||
roaming bool
|
||||
want string
|
||||
}{
|
||||
// Various combinations of application name casing, leading
|
||||
// period, operating system, and roaming flags.
|
||||
{"windows", appNameLower, false, winLocal},
|
||||
{"windows", appNameUpper, false, winLocal},
|
||||
{"windows", "." + appNameLower, false, winLocal},
|
||||
{"windows", "." + appNameUpper, false, winLocal},
|
||||
{"windows", appNameLower, true, winRoaming},
|
||||
{"windows", appNameUpper, true, winRoaming},
|
||||
{"windows", "." + appNameLower, true, winRoaming},
|
||||
{"windows", "." + appNameUpper, true, winRoaming},
|
||||
{"linux", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"linux", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"darwin", appNameLower, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", appNameUpper, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", "." + appNameLower, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"darwin", "." + appNameUpper, false, filepath.Join(macAppData, appNameUpper)},
|
||||
{"openbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"openbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"freebsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"netbsd", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"plan9", appNameLower, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", appNameUpper, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", "." + appNameLower, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"plan9", "." + appNameUpper, false, filepath.Join(homeDir, appNameLower)},
|
||||
{"unrecognized", appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", "." + appNameLower, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
{"unrecognized", "." + appNameUpper, false, filepath.Join(homeDir, "."+appNameLower)},
|
||||
|
||||
// No application name provided, so expect current directory.
|
||||
{"windows", "", false, "."},
|
||||
{"windows", "", true, "."},
|
||||
{"linux", "", false, "."},
|
||||
{"darwin", "", false, "."},
|
||||
{"openbsd", "", false, "."},
|
||||
{"freebsd", "", false, "."},
|
||||
{"netbsd", "", false, "."},
|
||||
{"plan9", "", false, "."},
|
||||
{"unrecognized", "", false, "."},
|
||||
|
||||
// Single dot provided for application name, so expect current
|
||||
// directory.
|
||||
{"windows", ".", false, "."},
|
||||
{"windows", ".", true, "."},
|
||||
{"linux", ".", false, "."},
|
||||
{"darwin", ".", false, "."},
|
||||
{"openbsd", ".", false, "."},
|
||||
{"freebsd", ".", false, "."},
|
||||
{"netbsd", ".", false, "."},
|
||||
{"plan9", ".", false, "."},
|
||||
{"unrecognized", ".", false, "."},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
ret := dcrutil.TstAppDataDir(test.goos, test.appName, test.roaming)
|
||||
if ret != test.want {
|
||||
t.Errorf("appDataDir #%d (%s) does not match - "+
|
||||
"expected got %s, want %s", i, test.goos, ret,
|
||||
test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
61
dcrutil/bitflags.go
Normal file
61
dcrutil/bitflags.go
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
// bitflags contains a simple series of functions to handle bitwise boolean
|
||||
// operations in uint16s.
|
||||
|
||||
// Flags16 is the type for 2 bytes of flags; not really used except in the
|
||||
// declaration below.
|
||||
type Flags16 uint16
|
||||
|
||||
// Flag fields for uint16.
|
||||
const (
|
||||
FlagNone Flags16 = 0
|
||||
BlockValid = 1 << 0 // Describes whether TxTreeRegular is valid
|
||||
Flag01 = 1 << 1
|
||||
Flag02 = 1 << 2
|
||||
Flag03 = 1 << 3
|
||||
Flag04 = 1 << 4
|
||||
Flag05 = 1 << 5
|
||||
Flag06 = 1 << 6
|
||||
Flag07 = 1 << 7
|
||||
Flag08 = 1 << 8
|
||||
Flag09 = 1 << 9
|
||||
Flag10 = 1 << 10
|
||||
Flag11 = 1 << 11
|
||||
Flag12 = 1 << 12
|
||||
Flag13 = 1 << 13
|
||||
Flag14 = 1 << 14
|
||||
Flag15 = 1 << 15
|
||||
)
|
||||
|
||||
// IsFlagSet16 returns true/false for a flag at flag field 'flag'.
|
||||
func IsFlagSet16(flags uint16, flag uint16) bool {
|
||||
return flags&flag != 0
|
||||
}
|
||||
|
||||
// SetFlag16 sets a bit flag at flag position 'flag' to bool 'b'.
|
||||
func SetFlag16(flags *uint16, flag uint16, b bool) {
|
||||
if b {
|
||||
*flags = *flags | flag
|
||||
} else {
|
||||
*flags = *flags &^ flag
|
||||
}
|
||||
}
|
||||
|
||||
// BoolArray16 is a bool array that is generated from a uint16 containing flags.
|
||||
type BoolArray16 [16]bool
|
||||
|
||||
// GenerateBoolArray16 generates a BoolArray16 from a uint16 containing flags.
|
||||
func GenerateBoolArray16(flags uint16) BoolArray16 {
|
||||
var ba BoolArray16
|
||||
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
ba[i] = (flags&(1<<i) != 0)
|
||||
}
|
||||
|
||||
return ba
|
||||
}
|
||||
414
dcrutil/block.go
Normal file
414
dcrutil/block.go
Normal file
@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// OutOfRangeError describes an error due to accessing an element that is out
|
||||
// of range.
|
||||
type OutOfRangeError string
|
||||
|
||||
// assertBlockImmutability throws a panic when a block has been
|
||||
// mutated.
|
||||
var assertBlockImmutability = false
|
||||
|
||||
// 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 cryptocurrency block that provides easier and more efficient
|
||||
// manipulation of raw 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 *wire.MsgBlock // Underlying MsgBlock
|
||||
serializedBlock []byte // Serialized bytes for the block
|
||||
hash chainhash.Hash // Cached block hash
|
||||
transactions []*Tx // Transactions
|
||||
sTransactions []*Tx // Stake transactions
|
||||
txnsGenerated bool // ALL wrapped transactions generated
|
||||
sTxnsGenerated bool // ALL wrapped stake transactions generated
|
||||
}
|
||||
|
||||
// MsgBlock returns the underlying wire.MsgBlock for the Block.
|
||||
func (b *Block) MsgBlock() *wire.MsgBlock {
|
||||
// Return the cached block.
|
||||
return b.msgBlock
|
||||
}
|
||||
|
||||
// Bytes returns the serialized bytes for the Block. This is equivalent to
|
||||
// calling Serialize on the underlying wire.MsgBlock, however it caches the
|
||||
// result so subsequent calls are more efficient.
|
||||
func (b *Block) Bytes() ([]byte, error) {
|
||||
// Return the cached serialized bytes if it has already been generated.
|
||||
if len(b.serializedBlock) != 0 {
|
||||
return b.serializedBlock, nil
|
||||
}
|
||||
|
||||
// Serialize the MsgBlock.
|
||||
var w bytes.Buffer
|
||||
w.Grow(b.msgBlock.SerializeSize())
|
||||
err := b.msgBlock.Serialize(&w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serializedBlock := w.Bytes()
|
||||
|
||||
// Cache the serialized bytes and return them.
|
||||
b.serializedBlock = serializedBlock
|
||||
return serializedBlock, nil
|
||||
}
|
||||
|
||||
// BlockHeaderBytes returns the serialized bytes for the Block's header. This is
|
||||
// equivalent to calling Serialize on the underlying wire.MsgBlock, but it
|
||||
// returns a byte slice.
|
||||
func (b *Block) BlockHeaderBytes() ([]byte, error) {
|
||||
// Return the cached serialized bytes if it has already been generated.
|
||||
if len(b.serializedBlock) != 0 {
|
||||
return b.serializedBlock, nil
|
||||
}
|
||||
|
||||
// Serialize the MsgBlock.
|
||||
var w bytes.Buffer
|
||||
w.Grow(wire.MaxBlockHeaderPayload)
|
||||
err := b.msgBlock.Header.Serialize(&w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serializedBlockHeader := w.Bytes()
|
||||
|
||||
// Cache the serialized bytes and return them.
|
||||
return serializedBlockHeader, nil
|
||||
}
|
||||
|
||||
// Hash returns the block identifier hash for the Block. This is equivalent to
|
||||
// calling BlockHash on the underlying wire.MsgBlock, however it caches the
|
||||
// result so subsequent calls are more efficient.
|
||||
func (b *Block) Hash() *chainhash.Hash {
|
||||
if assertBlockImmutability {
|
||||
hash := b.msgBlock.BlockHash()
|
||||
if !hash.IsEqual(&b.hash) {
|
||||
str := fmt.Sprintf("ASSERT: mutated util.block detected, old hash "+
|
||||
"%v, new hash %v", b.hash, hash)
|
||||
panic(str)
|
||||
}
|
||||
}
|
||||
|
||||
return &b.hash
|
||||
}
|
||||
|
||||
// Tx returns a wrapped transaction (dcrutil.Tx) for the transaction at the
|
||||
// specified index in the Block. The supplied index is 0 based. That is to
|
||||
// say, the first transaction in the block is txNum 0. This is nearly
|
||||
// equivalent to accessing the raw transaction (wire.MsgTx) from the
|
||||
// underlying wire.MsgBlock, however the wrapped transaction has some helpful
|
||||
// properties such as caching the hash so subsequent calls are more efficient.
|
||||
func (b *Block) Tx(txNum int) (*Tx, error) {
|
||||
// Ensure the requested transaction is in range.
|
||||
numTx := uint64(len(b.msgBlock.Transactions))
|
||||
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 wrapped transactions if needed.
|
||||
if len(b.transactions) == 0 {
|
||||
b.transactions = make([]*Tx, numTx)
|
||||
}
|
||||
|
||||
// Return the wrapped transaction if it has already been generated.
|
||||
if b.transactions[txNum] != nil {
|
||||
return b.transactions[txNum], nil
|
||||
}
|
||||
|
||||
// Generate and cache the wrapped transaction and return it.
|
||||
newTx := NewTx(b.msgBlock.Transactions[txNum])
|
||||
newTx.SetIndex(txNum)
|
||||
newTx.SetTree(wire.TxTreeRegular)
|
||||
b.transactions[txNum] = newTx
|
||||
return newTx, nil
|
||||
}
|
||||
|
||||
// STx returns a wrapped transaction (dcrutil.Tx) for the stake transaction at
|
||||
// the specified index in the Block. The supplied index is 0 based.
|
||||
func (b *Block) STx(txNum int) (*Tx, error) {
|
||||
// Ensure the requested transaction is in range.
|
||||
numTx := uint64(len(b.msgBlock.STransactions))
|
||||
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 wrapped transactions if needed.
|
||||
if len(b.sTransactions) == 0 {
|
||||
b.sTransactions = make([]*Tx, numTx)
|
||||
}
|
||||
|
||||
// Return the wrapped transaction if it has already been generated.
|
||||
if b.sTransactions[txNum] != nil {
|
||||
return b.sTransactions[txNum], nil
|
||||
}
|
||||
|
||||
// Generate and cache the wrapped transaction and return it.
|
||||
newTx := NewTx(b.msgBlock.STransactions[txNum])
|
||||
newTx.SetIndex(txNum)
|
||||
newTx.SetTree(wire.TxTreeStake)
|
||||
b.sTransactions[txNum] = newTx
|
||||
return newTx, nil
|
||||
}
|
||||
|
||||
// Transactions returns a slice of wrapped transactions (dcrutil.Tx) for all
|
||||
// transactions in the Block. This is nearly equivalent to accessing the raw
|
||||
// transactions (wire.MsgTx) in the underlying wire.MsgBlock, however it
|
||||
// instead provides easy access to wrapped versions (dcrutil.Tx) of them.
|
||||
func (b *Block) Transactions() []*Tx {
|
||||
// Return transactions if they have ALL already been generated. This
|
||||
// flag is necessary because the wrapped transactions are lazily
|
||||
// generated in a sparse fashion.
|
||||
if b.txnsGenerated {
|
||||
return b.transactions
|
||||
}
|
||||
|
||||
// Generate slice to hold all of the wrapped transactions if needed.
|
||||
if len(b.transactions) == 0 {
|
||||
b.transactions = make([]*Tx, len(b.msgBlock.Transactions))
|
||||
}
|
||||
|
||||
// Generate and cache the wrapped transactions for all that haven't
|
||||
// already been done.
|
||||
for i, tx := range b.transactions {
|
||||
if tx == nil {
|
||||
newTx := NewTx(b.msgBlock.Transactions[i])
|
||||
newTx.SetIndex(i)
|
||||
newTx.SetTree(wire.TxTreeRegular)
|
||||
b.transactions[i] = newTx
|
||||
}
|
||||
}
|
||||
|
||||
b.txnsGenerated = true
|
||||
return b.transactions
|
||||
}
|
||||
|
||||
// STransactions returns a slice of wrapped stake transactions (dcrutil.Tx) for all
|
||||
// stake transactions in the Block. This is nearly equivalent to accessing the raw
|
||||
// transactions (dcrwire.MsgTx) in the underlying wire.MsgBlock, however it
|
||||
// instead provides easy access to wrapped versions (util.Tx) of them.
|
||||
func (b *Block) STransactions() []*Tx {
|
||||
// Return transactions if they have ALL already been generated. This
|
||||
// flag is necessary because the wrapped transactions are lazily
|
||||
// generated in a sparse fashion.
|
||||
if b.sTxnsGenerated {
|
||||
return b.sTransactions
|
||||
}
|
||||
|
||||
// Generate slice to hold all of the wrapped transactions if needed.
|
||||
if len(b.sTransactions) == 0 {
|
||||
b.sTransactions = make([]*Tx, len(b.msgBlock.STransactions))
|
||||
}
|
||||
|
||||
// Generate and cache the wrapped transactions for all that haven't
|
||||
// already been done.
|
||||
for i, tx := range b.sTransactions {
|
||||
if tx == nil {
|
||||
newTx := NewTx(b.msgBlock.STransactions[i])
|
||||
newTx.SetIndex(i)
|
||||
newTx.SetTree(wire.TxTreeStake)
|
||||
b.sTransactions[i] = newTx
|
||||
}
|
||||
}
|
||||
|
||||
b.sTxnsGenerated = true
|
||||
return b.sTransactions
|
||||
}
|
||||
|
||||
// TxHash returns the hash for the requested transaction number in the Block.
|
||||
// The supplied index is 0 based. That is to say, the first transaction in the
|
||||
// block is txNum 0. This is equivalent to calling TxHash on the underlying
|
||||
// wire.MsgTx, however it caches the result so subsequent calls are more
|
||||
// efficient.
|
||||
func (b *Block) TxHash(txNum int) (*chainhash.Hash, error) {
|
||||
// Attempt to get a wrapped transaction for the specified index. It
|
||||
// will be created lazily if needed or simply return the cached version
|
||||
// if it has already been generated.
|
||||
tx, err := b.Tx(txNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Defer to the wrapped transaction which will return the cached hash if
|
||||
// it has already been generated.
|
||||
return tx.Hash(), nil
|
||||
}
|
||||
|
||||
// STxHash returns the hash for the requested stake transaction number in the
|
||||
// Block. The supplied index is 0 based. That is to say, the first transaction
|
||||
// in the block is txNum 0. This is equivalent to calling TxHash on the
|
||||
// underlying wire.MsgTx, however it caches the result so subsequent calls are
|
||||
// more efficient.
|
||||
func (b *Block) STxHash(txNum int) (*chainhash.Hash, error) {
|
||||
// Attempt to get a wrapped transaction for the specified index. It
|
||||
// will be created lazily if needed or simply return the cached version
|
||||
// if it has already been generated.
|
||||
tx, err := b.STx(txNum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Defer to the wrapped transaction which will return the cached hash if
|
||||
// it has already been generated.
|
||||
return tx.Hash(), nil
|
||||
}
|
||||
|
||||
// TxLoc returns the offsets and lengths of each transaction in a raw block.
|
||||
// It is used to allow fast indexing into transactions within the raw byte
|
||||
// stream.
|
||||
func (b *Block) TxLoc() ([]wire.TxLoc, []wire.TxLoc, error) {
|
||||
rawMsg, err := b.Bytes()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rbuf := bytes.NewBuffer(rawMsg)
|
||||
|
||||
var mblock wire.MsgBlock
|
||||
txLocs, sTxLocs, err := mblock.DeserializeTxLoc(rbuf)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return txLocs, sTxLocs, err
|
||||
}
|
||||
|
||||
// Height returns a casted int64 height from the block header.
|
||||
//
|
||||
// This function should not be used for new code and will be
|
||||
// removed in the future.
|
||||
func (b *Block) Height() int64 {
|
||||
return int64(b.msgBlock.Header.Height)
|
||||
}
|
||||
|
||||
// NewBlock returns a new instance of a block given an underlying
|
||||
// wire.MsgBlock. See Block.
|
||||
func NewBlock(msgBlock *wire.MsgBlock) *Block {
|
||||
return &Block{
|
||||
hash: msgBlock.BlockHash(),
|
||||
msgBlock: msgBlock,
|
||||
}
|
||||
}
|
||||
|
||||
// NewBlockDeepCopyCoinbase returns a new instance of a block given an underlying
|
||||
// wire.MsgBlock, but makes a deep copy of the coinbase transaction since it's
|
||||
// sometimes mutable.
|
||||
func NewBlockDeepCopyCoinbase(msgBlock *wire.MsgBlock) *Block {
|
||||
// Copy the msgBlock and the pointers to all the transactions.
|
||||
msgBlockCopy := new(wire.MsgBlock)
|
||||
|
||||
lenTxs := len(msgBlock.Transactions)
|
||||
mtxsCopy := make([]*wire.MsgTx, lenTxs)
|
||||
for i, mtx := range msgBlock.Transactions {
|
||||
mtxsCopy[i] = mtx
|
||||
}
|
||||
msgBlockCopy.Transactions = mtxsCopy
|
||||
lenStxs := len(msgBlock.STransactions)
|
||||
smtxsCopy := make([]*wire.MsgTx, lenStxs)
|
||||
for i, smtx := range msgBlock.STransactions {
|
||||
smtxsCopy[i] = smtx
|
||||
}
|
||||
msgBlockCopy.STransactions = smtxsCopy
|
||||
msgBlockCopy.Header = msgBlock.Header
|
||||
|
||||
// Deep copy the first transaction. Also change the coinbase pointer.
|
||||
msgBlockCopy.Transactions[0] =
|
||||
NewTxDeep(msgBlockCopy.Transactions[0]).MsgTx()
|
||||
|
||||
bl := &Block{
|
||||
msgBlock: msgBlockCopy,
|
||||
}
|
||||
bl.hash = msgBlock.BlockHash()
|
||||
|
||||
return bl
|
||||
}
|
||||
|
||||
// NewBlockDeepCopy deep copies an entire block down to the wire components and
|
||||
// returns the new block based off of this copy.
|
||||
func NewBlockDeepCopy(msgBlock *wire.MsgBlock) *Block {
|
||||
// Deep copy the header and all the transactions.
|
||||
msgBlockCopy := new(wire.MsgBlock)
|
||||
lenTxs := len(msgBlock.Transactions)
|
||||
mtxsCopy := make([]*wire.MsgTx, lenTxs)
|
||||
for i, mtx := range msgBlock.Transactions {
|
||||
txd := NewTxDeep(mtx)
|
||||
mtxsCopy[i] = txd.MsgTx()
|
||||
}
|
||||
msgBlockCopy.Transactions = mtxsCopy
|
||||
lenStxs := len(msgBlock.STransactions)
|
||||
smtxsCopy := make([]*wire.MsgTx, lenStxs)
|
||||
for i, smtx := range msgBlock.STransactions {
|
||||
stxd := NewTxDeep(smtx)
|
||||
smtxsCopy[i] = stxd.MsgTx()
|
||||
}
|
||||
msgBlockCopy.STransactions = smtxsCopy
|
||||
msgBlockCopy.Header = msgBlock.Header
|
||||
|
||||
bl := &Block{
|
||||
msgBlock: msgBlockCopy,
|
||||
}
|
||||
bl.hash = msgBlock.BlockHash()
|
||||
|
||||
return bl
|
||||
}
|
||||
|
||||
// NewBlockFromBytes returns a new instance of a block given the
|
||||
// serialized bytes. See Block.
|
||||
func NewBlockFromBytes(serializedBlock []byte) (*Block, error) {
|
||||
br := bytes.NewReader(serializedBlock)
|
||||
b, err := NewBlockFromReader(br)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.serializedBlock = serializedBlock
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// NewBlockFromReader returns a new instance of a block given a
|
||||
// Reader to deserialize the block. See Block.
|
||||
func NewBlockFromReader(r io.Reader) (*Block, error) {
|
||||
// Deserialize the bytes into a MsgBlock.
|
||||
var msgBlock wire.MsgBlock
|
||||
err := msgBlock.Deserialize(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := Block{
|
||||
hash: msgBlock.BlockHash(),
|
||||
msgBlock: &msgBlock,
|
||||
}
|
||||
return &b, nil
|
||||
}
|
||||
|
||||
// NewBlockFromBlockAndBytes returns a new instance of a block given
|
||||
// an underlying wire.MsgBlock and the serialized bytes for it. See Block.
|
||||
func NewBlockFromBlockAndBytes(msgBlock *wire.MsgBlock, serializedBlock []byte) *Block {
|
||||
return &Block{
|
||||
hash: msgBlock.BlockHash(),
|
||||
msgBlock: msgBlock,
|
||||
serializedBlock: serializedBlock,
|
||||
}
|
||||
}
|
||||
562
dcrutil/block_test.go
Normal file
562
dcrutil/block_test.go
Normal file
@ -0,0 +1,562 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// TestBlock tests the API for Block.
|
||||
func TestBlock(t *testing.T) {
|
||||
b := dcrutil.NewBlock(&Block100000)
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
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)
|
||||
if gotHeight := b.Height(); gotHeight != wantHeight {
|
||||
t.Errorf("Height: mismatched height - got %v, want %v",
|
||||
gotHeight, wantHeight)
|
||||
}
|
||||
|
||||
// Hash for block 100,000.
|
||||
wantHashStr := "142c5f5b6f868b0e70172b78cea2cff21e6580612b3a360cf6bb2a5976e25ed1"
|
||||
wantHash, err := chainhash.NewHashFromStr(wantHashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Request the hash multiple times to test generation and caching.
|
||||
for i := 0; i < 2; i++ {
|
||||
hash := b.Hash()
|
||||
if !hash.IsEqual(wantHash) {
|
||||
t.Errorf("Hash #%d mismatched hash - got %v, want %v",
|
||||
i, hash, wantHash)
|
||||
}
|
||||
}
|
||||
|
||||
// Hashes for the transactions in Block100000.
|
||||
wantTxHashes := []string{
|
||||
"1cbd9fe1a143a265cc819ff9d8132a7cbc4ca48eb68c0de39cfdf7ecf42cbbd1",
|
||||
"f3f9bc9473b6fe18d66e3ac2a1a95b6317b280f4e6687a074075b56aebf1eb53",
|
||||
"ba2ed6210a561a4dab34ec8668ad61ec97f126826dae893719dff7383b9d6928",
|
||||
"c5dde35b55b856cf73b2d85737c68b0dcfdaad01d0271ee509f3a7efacc025b3",
|
||||
}
|
||||
|
||||
// Create a new block to nuke all cached data.
|
||||
b = dcrutil.NewBlock(&Block100000)
|
||||
|
||||
// Request hash for all transactions one at a time via Tx.
|
||||
for i, txHash := range wantTxHashes {
|
||||
wantHash, err := chainhash.NewHashFromStr(txHash)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Request the hash multiple times to test generation and
|
||||
// caching.
|
||||
for j := 0; j < 2; j++ {
|
||||
tx, err := b.Tx(i)
|
||||
if err != nil {
|
||||
t.Errorf("Tx #%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
hash := tx.Hash()
|
||||
if !hash.IsEqual(wantHash) {
|
||||
t.Errorf("Hash #%d mismatched hash - got %v, "+
|
||||
"want %v", j, hash, wantHash)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new block to nuke all cached data.
|
||||
b = dcrutil.NewBlock(&Block100000)
|
||||
|
||||
// Request slice of all transactions multiple times to test generation
|
||||
// and caching.
|
||||
for i := 0; i < 2; i++ {
|
||||
transactions := b.Transactions()
|
||||
|
||||
// Ensure we get the expected number of transactions.
|
||||
if len(transactions) != len(wantTxHashes) {
|
||||
t.Errorf("Transactions #%d mismatched number of "+
|
||||
"transactions - got %d, want %d", i,
|
||||
len(transactions), len(wantTxHashes))
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure all of the hashes match.
|
||||
for j, tx := range transactions {
|
||||
wantHash, err := chainhash.NewHashFromStr(wantTxHashes[j])
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
hash := tx.Hash()
|
||||
if !hash.IsEqual(wantHash) {
|
||||
t.Errorf("Transactions #%d mismatched hashes "+
|
||||
"- got %v, want %v", j, hash, wantHash)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
block100000Buf.Grow(Block100000.SerializeSize())
|
||||
err = Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Request serialized bytes multiple times to test generation and
|
||||
// caching.
|
||||
for i := 0; i < 2; i++ {
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes #%d wrong bytes - got %v, want %v", i,
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction offsets and length for the transaction in Block100000.
|
||||
wantTxLocs := []wire.TxLoc{
|
||||
{TxStart: 181, TxLen: 159},
|
||||
{TxStart: 340, TxLen: 285},
|
||||
{TxStart: 625, TxLen: 283},
|
||||
{TxStart: 908, TxLen: 249},
|
||||
}
|
||||
|
||||
// Ensure the transaction location information is accurate.
|
||||
txLocs, _, err := b.TxLoc()
|
||||
if err != nil {
|
||||
t.Errorf("TxLoc: %v", err)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(txLocs, wantTxLocs) {
|
||||
t.Errorf("TxLoc: mismatched transaction location information "+
|
||||
"- got %v, want %v", spew.Sdump(txLocs),
|
||||
spew.Sdump(wantTxLocs))
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewBlockFromBytes tests creation of a Block from serialized bytes.
|
||||
func TestNewBlockFromBytes(t *testing.T) {
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
block100000Buf.Grow(Block100000.SerializeSize())
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the serialized bytes.
|
||||
b, err := dcrutil.NewBlockFromBytes(block100000Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("NewBlockFromBytes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes: wrong bytes - got %v, want %v",
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
block100000Buf.Grow(Block100000.SerializeSize())
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the serialized bytes.
|
||||
b := dcrutil.NewBlockFromBlockAndBytes(&Block100000, block100000Bytes)
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
serializedBytes, err := b.Bytes()
|
||||
if err != nil {
|
||||
t.Errorf("Bytes: %v", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(serializedBytes, block100000Bytes) {
|
||||
t.Errorf("Bytes: wrong bytes - got %v, want %v",
|
||||
spew.Sdump(serializedBytes),
|
||||
spew.Sdump(block100000Bytes))
|
||||
}
|
||||
if msgBlock := b.MsgBlock(); !reflect.DeepEqual(msgBlock, &Block100000) {
|
||||
t.Errorf("MsgBlock: mismatched MsgBlock - got %v, want %v",
|
||||
spew.Sdump(msgBlock), spew.Sdump(&Block100000))
|
||||
}
|
||||
}
|
||||
|
||||
// TestBlockErrors tests the error paths for the Block API.
|
||||
func TestBlockErrors(t *testing.T) {
|
||||
// Ensure out of range errors are as expected.
|
||||
wantErr := "transaction index -1 is out of range - max 3"
|
||||
testErr := dcrutil.OutOfRangeError(wantErr)
|
||||
if testErr.Error() != wantErr {
|
||||
t.Errorf("OutOfRangeError: wrong error - got %v, want %v",
|
||||
testErr.Error(), wantErr)
|
||||
}
|
||||
|
||||
// Serialize the test block.
|
||||
var block100000Buf bytes.Buffer
|
||||
block100000Buf.Grow(Block100000.SerializeSize())
|
||||
err := Block100000.Serialize(&block100000Buf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
block100000Bytes := block100000Buf.Bytes()
|
||||
|
||||
// Create a new block from the serialized bytes.
|
||||
b, err := dcrutil.NewBlockFromBytes(block100000Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("NewBlockFromBytes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Truncate the block byte buffer to force errors.
|
||||
shortBytes := block100000Bytes[:100]
|
||||
_, err = dcrutil.NewBlockFromBytes(shortBytes)
|
||||
if err != io.EOF {
|
||||
t.Errorf("NewBlockFromBytes: did not get expected error - "+
|
||||
"got %v, want %v", err, io.EOF)
|
||||
}
|
||||
|
||||
// Ensure TxHash returns expected error on invalid indices.
|
||||
_, err = b.TxHash(-1)
|
||||
if _, ok := err.(dcrutil.OutOfRangeError); !ok {
|
||||
t.Errorf("TxHash: wrong error - got: %v <%T>, "+
|
||||
"want: <%T>", err, err, dcrutil.OutOfRangeError(""))
|
||||
}
|
||||
_, err = b.TxHash(len(Block100000.Transactions) + 1)
|
||||
if _, ok := err.(dcrutil.OutOfRangeError); !ok {
|
||||
t.Errorf("TxHash: wrong error - got: %v <%T>, "+
|
||||
"want: <%T>", err, err, dcrutil.OutOfRangeError(""))
|
||||
}
|
||||
|
||||
// Ensure Tx returns expected error on invalid indices.
|
||||
_, err = b.Tx(-1)
|
||||
if _, ok := err.(dcrutil.OutOfRangeError); !ok {
|
||||
t.Errorf("Tx: wrong error - got: %v <%T>, "+
|
||||
"want: <%T>", err, err, dcrutil.OutOfRangeError(""))
|
||||
}
|
||||
_, err = b.Tx(len(Block100000.Transactions) + 1)
|
||||
if _, ok := err.(dcrutil.OutOfRangeError); !ok {
|
||||
t.Errorf("Tx: wrong error - got: %v <%T>, "+
|
||||
"want: <%T>", err, err, dcrutil.OutOfRangeError(""))
|
||||
}
|
||||
|
||||
// Ensure TxLoc returns expected error with short byte buffer.
|
||||
// This makes use of the test package only function, SetBlockBytes, to
|
||||
// inject a short byte buffer.
|
||||
b.SetBlockBytes(shortBytes)
|
||||
_, _, err = b.TxLoc()
|
||||
if err != io.EOF {
|
||||
t.Errorf("TxLoc: did not get expected error - "+
|
||||
"got %v, want %v", err, io.EOF)
|
||||
}
|
||||
}
|
||||
|
||||
// Block100000 defines block 100,000 of the block chain. It is used to
|
||||
// test Block operations.
|
||||
var Block100000 = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
Height: 100000,
|
||||
PrevBlock: chainhash.Hash([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: chainhash.Hash([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
|
||||
},
|
||||
Transactions: []*wire.MsgTx{
|
||||
{
|
||||
SerType: wire.TxSerializeFull,
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{},
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x04, 0x4c, 0x86, 0x04, 0x1b, 0x02, 0x06, 0x02,
|
||||
},
|
||||
Sequence: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOut: []*wire.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,
|
||||
},
|
||||
{
|
||||
SerType: wire.TxSerializeFull,
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash([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: []*wire.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
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
SerType: wire.TxSerializeFull,
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash([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: []*wire.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
|
||||
},
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
SerType: wire.TxSerializeFull,
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash([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: []*wire.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,
|
||||
},
|
||||
},
|
||||
STransactions: []*wire.MsgTx{},
|
||||
}
|
||||
18
dcrutil/const.go
Normal file
18
dcrutil/const.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
const (
|
||||
// AtomsPerCent is the number of atomic units in one coin cent.
|
||||
AtomsPerCent = 1e6
|
||||
|
||||
// AtomsPerCoin is the number of atomic units in one coin.
|
||||
AtomsPerCoin = 1e8
|
||||
|
||||
// MaxAmount is the maximum transaction amount allowed in atoms.
|
||||
// Decred - Changeme for release
|
||||
MaxAmount = 21e6 * AtomsPerCoin
|
||||
)
|
||||
47
dcrutil/doc.go
Normal file
47
dcrutil/doc.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package dcrutil provides decred-specific convenience functions and types.
|
||||
|
||||
Block Overview
|
||||
|
||||
A Block defines a decred 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.
|
||||
|
||||
Tx Overview
|
||||
|
||||
A Tx defines a decred transaction that provides more efficient manipulation of
|
||||
raw wire protocol transactions. It memoizes the hash for the transaction on its
|
||||
first access so subsequent accesses don't have to repeat the relatively
|
||||
expensive hashing operations.
|
||||
|
||||
Address Overview
|
||||
|
||||
The Address interface provides an abstraction for a Decred address. While the
|
||||
most common type is a pay-to-pubkey-hash, Decred already supports others and
|
||||
may well support more in the future. This package currently provides
|
||||
implementations for the pay-to-pubkey, pay-to-pubkey-hash, and
|
||||
pay-to-script-hash address types.
|
||||
|
||||
To decode/encode an address:
|
||||
|
||||
// NOTE: The default network is only used for address types which do not
|
||||
// already contain that information. At this time, that is only
|
||||
// pay-to-pubkey addresses.
|
||||
addrString := "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962" +
|
||||
"e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d57" +
|
||||
"8a4c702b6bf11d5f"
|
||||
defaultNet := &chaincfg.MainNetParams
|
||||
addr, err := dcrutil.DecodeAddress(addrString, defaultNet)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(addr.EncodeAddress())
|
||||
*/
|
||||
package dcrutil
|
||||
76
dcrutil/example_test.go
Normal file
76
dcrutil/example_test.go
Normal file
@ -0,0 +1,76 @@
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
func ExampleAmount() {
|
||||
|
||||
a := dcrutil.Amount(0)
|
||||
fmt.Println("Zero Atom:", a)
|
||||
|
||||
a = dcrutil.Amount(1e8)
|
||||
fmt.Println("100,000,000 Atoms:", a)
|
||||
|
||||
a = dcrutil.Amount(1e5)
|
||||
fmt.Println("100,000 Atoms:", a)
|
||||
// Output:
|
||||
// Zero Atom: 0 DCR
|
||||
// 100,000,000 Atoms: 1 DCR
|
||||
// 100,000 Atoms: 0.001 DCR
|
||||
}
|
||||
|
||||
func ExampleNewAmount() {
|
||||
amountOne, err := dcrutil.NewAmount(1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(amountOne) //Output 1
|
||||
|
||||
amountFraction, err := dcrutil.NewAmount(0.01234567)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(amountFraction) //Output 2
|
||||
|
||||
amountZero, err := dcrutil.NewAmount(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(amountZero) //Output 3
|
||||
|
||||
amountNaN, err := dcrutil.NewAmount(math.NaN())
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(amountNaN) //Output 4
|
||||
|
||||
// Output: 1 DCR
|
||||
// 0.01234567 DCR
|
||||
// 0 DCR
|
||||
// invalid coin amount
|
||||
}
|
||||
|
||||
func ExampleAmount_unitConversions() {
|
||||
amount := dcrutil.Amount(44433322211100)
|
||||
|
||||
fmt.Println("Atom to kCoin:", amount.Format(dcrutil.AmountKiloCoin))
|
||||
fmt.Println("Atom to Coin:", amount)
|
||||
fmt.Println("Atom to MilliCoin:", amount.Format(dcrutil.AmountMilliCoin))
|
||||
fmt.Println("Atom to MicroCoin:", amount.Format(dcrutil.AmountMicroCoin))
|
||||
fmt.Println("Atom to Atom:", amount.Format(dcrutil.AmountAtom))
|
||||
|
||||
// Output:
|
||||
// Atom to kCoin: 444.333222111 kDCR
|
||||
// Atom to Coin: 444333.222111 DCR
|
||||
// Atom to MilliCoin: 444333222.111 mDCR
|
||||
// Atom to MicroCoin: 444333222111 μDCR
|
||||
// Atom to Atom: 44433322211100 Atom
|
||||
}
|
||||
25
dcrutil/hash160.go
Normal file
25
dcrutil/hash160.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"hash"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// Calculate the hash of hasher over buf.
|
||||
func calcHash(buf []byte, hasher hash.Hash) []byte {
|
||||
hasher.Write(buf)
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
// Hash160 calculates the hash ripemd160(hash256(b)).
|
||||
func Hash160(buf []byte) []byte {
|
||||
return calcHash(chainhash.HashB(buf), ripemd160.New())
|
||||
}
|
||||
59
dcrutil/hdkeychain/README.md
Normal file
59
dcrutil/hdkeychain/README.md
Normal file
@ -0,0 +1,59 @@
|
||||
hdkeychain
|
||||
==========
|
||||
|
||||
[](https://travis-ci.org/decred/dcrd)
|
||||
[](http://copyfree.org)
|
||||
[](http://godoc.org/github.com/decred/dcrd/dcrutil/hdkeychain)
|
||||
|
||||
Package hdkeychain provides an API for Decred hierarchical deterministic
|
||||
extended keys (based on BIP0032).
|
||||
|
||||
A comprehensive suite of tests is provided to ensure proper functionality. 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.
|
||||
|
||||
## Feature Overview
|
||||
|
||||
- Full BIP0032 implementation
|
||||
- Single type for private and public extended keys
|
||||
- Convenient cryptograpically secure seed generation
|
||||
- Simple creation of master nodes
|
||||
- Support for multi-layer derivation
|
||||
- Easy serialization and deserialization for both private and public extended
|
||||
keys
|
||||
- Support for custom networks by registering them with chaincfg
|
||||
- Obtaining the underlying EC pubkeys, EC privkeys, and associated decred
|
||||
addresses ties in seamlessly with existing btcec and dcrutil types which
|
||||
provide powerful tools for working with them to do things like sign
|
||||
transations and generate payment scripts
|
||||
- Uses the btcec package which is highly optimized for secp256k1
|
||||
- Code examples including:
|
||||
- Generating a cryptographically secure random seed and deriving a
|
||||
master node from it
|
||||
- Default HD wallet layout as described by BIP0032
|
||||
- Audits use case as described by BIP0032
|
||||
- Comprehensive test coverage including the BIP0032 test vectors
|
||||
- Benchmarks
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/decred/dcrd/dcrutil/hdkeychain
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [NewMaster Example](http://godoc.org/github.com/decred/dcrd/dcrutil/hdkeychain#example-NewMaster)
|
||||
Demonstrates how to generate a cryptographically random seed then use it to
|
||||
create a new master node (extended key).
|
||||
* [Default Wallet Layout Example](http://godoc.org/github.com/decred/dcrd/dcrutil/hdkeychain#example-package--DefaultWalletLayout)
|
||||
Demonstrates the default hierarchical deterministic wallet layout as described
|
||||
in BIP0032.
|
||||
* [Audits Use Case Example](http://godoc.org/github.com/decred/dcrd/dcrutil/hdkeychain#example-package--Audits)
|
||||
Demonstrates the audits use case in BIP0032.
|
||||
|
||||
## License
|
||||
|
||||
Package hdkeychain is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
||||
85
dcrutil/hdkeychain/bench_test.go
Normal file
85
dcrutil/hdkeychain/bench_test.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil/hdkeychain"
|
||||
)
|
||||
|
||||
// bip0032MasterPriv1 is the master private extended key from the first set of
|
||||
// test vectors in BIP0032.
|
||||
const bip0032MasterPriv1 = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbP" +
|
||||
"y6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
|
||||
|
||||
// BenchmarkDeriveHardened benchmarks how long it takes to derive a hardened
|
||||
// child from a master private extended key.
|
||||
func BenchmarkDeriveHardened(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Child(hdkeychain.HardenedKeyStart)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDeriveNormal benchmarks how long it takes to derive a normal
|
||||
// (non-hardened) child from a master private extended key.
|
||||
func BenchmarkDeriveNormal(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Child(0)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkPrivToPub benchmarks how long it takes to convert a private extended
|
||||
// key to a public extended key.
|
||||
func BenchmarkPrivToPub(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.Neuter()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDeserialize benchmarks how long it takes to deserialize a private
|
||||
// extended key.
|
||||
func BenchmarkDeserialize(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSerialize benchmarks how long it takes to serialize a private
|
||||
// extended key.
|
||||
func BenchmarkSerialize(b *testing.B) {
|
||||
b.StopTimer()
|
||||
masterKey, err := hdkeychain.NewKeyFromString(bip0032MasterPriv1)
|
||||
if err != nil {
|
||||
b.Errorf("Failed to decode master seed: %v", err)
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
masterKey.String()
|
||||
}
|
||||
}
|
||||
85
dcrutil/hdkeychain/doc.go
Normal file
85
dcrutil/hdkeychain/doc.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package hdkeychain provides an API for decred hierarchical deterministic
|
||||
extended keys (based on BIP0032).
|
||||
|
||||
Overview
|
||||
|
||||
The ability to implement hierarchical deterministic wallets depends on the
|
||||
ability to create and derive hierarchical deterministic extended keys.
|
||||
|
||||
At a high level, this package provides support for those hierarchical
|
||||
deterministic extended keys by providing an ExtendedKey type and supporting
|
||||
functions. Each extended key can either be a private or public extended key
|
||||
which itself is capable of deriving a child extended key.
|
||||
|
||||
Determining the Extended Key Type
|
||||
|
||||
Whether an extended key is a private or public extended key can be determined
|
||||
with the IsPrivate function.
|
||||
|
||||
Transaction Signing Keys and Payment Addresses
|
||||
|
||||
In order to create and sign transactions, or provide others with addresses to
|
||||
send funds to, the underlying key and address material must be accessible. This
|
||||
package provides the ECPubKey, ECPrivKey, and Address functions for this
|
||||
purpose.
|
||||
|
||||
The Master Node
|
||||
|
||||
As previously mentioned, the extended keys are hierarchical meaning they are
|
||||
used to form a tree. The root of that tree is called the master node and this
|
||||
package provides the NewMaster function to create it from a cryptographically
|
||||
random seed. The GenerateSeed function is provided as a convenient way to
|
||||
create a random seed for use with the NewMaster function.
|
||||
|
||||
Deriving Children
|
||||
|
||||
Once you have created a tree root (or have deserialized an extended key as
|
||||
discussed later), the child extended keys can be derived by using the Child
|
||||
function. The Child function supports deriving both normal (non-hardened) and
|
||||
hardened child extended keys. In order to derive a hardened extended key, use
|
||||
the HardenedKeyStart constant + the hardened key number as the index to the
|
||||
Child function. This provides the ability to cascade the keys into a tree and
|
||||
hence generate the hierarchical deterministic key chains.
|
||||
|
||||
Normal vs Hardened Child Extended Keys
|
||||
|
||||
A private extended key can be used to derive both hardened and non-hardened
|
||||
(normal) child private and public extended keys. A public extended key can only
|
||||
be used to derive non-hardened child public extended keys. As enumerated in
|
||||
BIP0032 "knowledge of the extended public key plus any non-hardened private key
|
||||
descending from it is equivalent to knowing the extended private key (and thus
|
||||
every private and public key descending from it). This means that extended
|
||||
public keys must be treated more carefully than regular public keys. It is also
|
||||
the reason for the existence of hardened keys, and why they are used for the
|
||||
account level in the tree. This way, a leak of an account-specific (or below)
|
||||
private key never risks compromising the master or other accounts."
|
||||
|
||||
Neutering a Private Extended Key
|
||||
|
||||
A private extended key can be converted to a new instance of the corresponding
|
||||
public extended key with the Neuter function. The original extended key is not
|
||||
modified. A public extended key is still capable of deriving non-hardened child
|
||||
public extended keys.
|
||||
|
||||
Serializing and Deserializing Extended Keys
|
||||
|
||||
Extended keys are serialized and deserialized with the String and
|
||||
NewKeyFromString functions. The serialized key is a Base58-encoded string which
|
||||
looks like the following:
|
||||
public key: xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw
|
||||
private key: xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7
|
||||
|
||||
Network
|
||||
|
||||
Extended keys are much like normal Decred addresses in that they have version
|
||||
bytes which tie them to a specific network. The SetNet and IsForNet functions
|
||||
are provided to set and determinine which network an extended key is associated
|
||||
with.
|
||||
*/
|
||||
package hdkeychain
|
||||
187
dcrutil/hdkeychain/example_test.go
Normal file
187
dcrutil/hdkeychain/example_test.go
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/dcrutil/hdkeychain"
|
||||
)
|
||||
|
||||
// This example demonstrates how to generate a cryptographically random seed
|
||||
// then use it to create a new master node (extended key).
|
||||
func ExampleNewMaster() {
|
||||
// Generate a random seed at the recommended length.
|
||||
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate a new master node using the seed.
|
||||
key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Show that the generated master node extended key is private.
|
||||
fmt.Println("Private Extended Key?:", key.IsPrivate())
|
||||
|
||||
// Output:
|
||||
// Private Extended Key?: true
|
||||
}
|
||||
|
||||
// This example demonstrates the default hierarchical deterministic wallet
|
||||
// layout as described in BIP0032.
|
||||
func Example_defaultWalletLayout() {
|
||||
// The default wallet layout described in BIP0032 is:
|
||||
//
|
||||
// Each account is composed of two keypair chains: an internal and an
|
||||
// external one. The external keychain is used to generate new public
|
||||
// addresses, while the internal keychain is used for all other
|
||||
// operations (change addresses, generation addresses, ..., anything
|
||||
// that doesn't need to be communicated).
|
||||
//
|
||||
// * m/iH/0/k
|
||||
// corresponds to the k'th keypair of the external chain of account
|
||||
// number i of the HDW derived from master m.
|
||||
// * m/iH/1/k
|
||||
// corresponds to the k'th keypair of the internal chain of account
|
||||
// number i of the HDW derived from master m.
|
||||
|
||||
// Ordinarily this would either be read from some encrypted source
|
||||
// and be decrypted or generated as the NewMaster example shows, but
|
||||
// for the purposes of this example, the private exteded key for the
|
||||
// master node is being hard coded here.
|
||||
master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
|
||||
"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"
|
||||
|
||||
// Start by getting an extended key instance for the master node.
|
||||
// This gives the path:
|
||||
// m
|
||||
masterKey, err := hdkeychain.NewKeyFromString(master)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for account 0. This gives the path:
|
||||
// m/0H
|
||||
acct0, err := masterKey.Child(hdkeychain.HardenedKeyStart + 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for the account 0 external chain. This
|
||||
// gives the path:
|
||||
// m/0H/0
|
||||
acct0Ext, err := acct0.Child(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the extended key for the account 0 internal chain. This gives
|
||||
// the path:
|
||||
// m/0H/1
|
||||
acct0Int, err := acct0.Child(1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, acct0Ext and acct0Int are ready to derive the keys for
|
||||
// the external and internal wallet chains.
|
||||
|
||||
// Derive the 10th extended key for the account 0 external chain. This
|
||||
// gives the path:
|
||||
// m/0H/0/10
|
||||
acct0Ext10, err := acct0Ext.Child(10)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Derive the 1st extended key for the account 0 internal chain. This
|
||||
// gives the path:
|
||||
// m/0H/1/0
|
||||
acct0Int0, err := acct0Int.Child(0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get and show the address associated with the extended keys for the
|
||||
// main decred network.
|
||||
acct0ExtAddr, err := acct0Ext10.Address(&chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
acct0IntAddr, err := acct0Int0.Address(&chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println("Account 0 External Address 10:", acct0ExtAddr)
|
||||
fmt.Println("Account 0 Internal Address 0:", acct0IntAddr)
|
||||
|
||||
// Output:
|
||||
// Account 0 External Address 10: DshMmJ3bfvMDdk1mkXRD3x5xDuPwSxoYGfi
|
||||
// Account 0 Internal Address 0: DsoTyktAyEDkYpgKSex6zx5rrkFDi2gAsHr
|
||||
}
|
||||
|
||||
// This example demonstrates the audits use case in BIP0032.
|
||||
func Example_audits() {
|
||||
// The audits use case described in BIP0032 is:
|
||||
//
|
||||
// In case an auditor needs full access to the list of incoming and
|
||||
// outgoing payments, one can share all account public extended keys.
|
||||
// This will allow the auditor to see all transactions from and to the
|
||||
// wallet, in all accounts, but not a single secret key.
|
||||
//
|
||||
// * N(m/*)
|
||||
// corresponds to the neutered master extended key (also called
|
||||
// the master public extended key)
|
||||
|
||||
// Ordinarily this would either be read from some encrypted source
|
||||
// and be decrypted or generated as the NewMaster example shows, but
|
||||
// for the purposes of this example, the private exteded key for the
|
||||
// master node is being hard coded here.
|
||||
master := "dprv3hCznBesA6jBushjx7y9NrfheE4ZshnaKYtsoLXefmLPzrXgEiXkd" +
|
||||
"RMD6UngnmBYZzgNhdEd4K3PidxcaCiR6HC9hmpj8FcrP4Cv7zBwELA"
|
||||
|
||||
// Start by getting an extended key instance for the master node.
|
||||
// This gives the path:
|
||||
// m
|
||||
masterKey, err := hdkeychain.NewKeyFromString(master)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Neuter the master key to generate a master public extended key. This
|
||||
// gives the path:
|
||||
// N(m/*)
|
||||
masterPubKey, err := masterKey.Neuter()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Share the master public extended key with the auditor.
|
||||
mpks, err := masterPubKey.String()
|
||||
if err != nil {
|
||||
panic("unexpected error creating string of extended public key")
|
||||
}
|
||||
fmt.Println("Audit key N(m/*):", mpks)
|
||||
|
||||
// Output:
|
||||
// Audit key N(m/*): dpubZ9169KDAEUnypHbWCe2Vu5TxGEcqJeNeX6XCYFU1fqw2iQZK7fsMhzsEFArbLmyUdprUw9aXHneUNd92bjc31TqC6sUduMY6PK2z4JXDS8j
|
||||
}
|
||||
558
dcrutil/hdkeychain/extendedkey.go
Normal file
558
dcrutil/hdkeychain/extendedkey.go
Normal file
@ -0,0 +1,558 @@
|
||||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain
|
||||
|
||||
// References:
|
||||
// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/decred/base58"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// RecommendedSeedLen is the recommended length in bytes for a seed
|
||||
// to a master node.
|
||||
RecommendedSeedLen = 32 // 256 bits
|
||||
|
||||
// HardenedKeyStart is the index at which a hardended key starts. Each
|
||||
// extended key has 2^31 normal child keys and 2^31 hardned child keys.
|
||||
// Thus the range for normal child keys is [0, 2^31 - 1] and the range
|
||||
// for hardened child keys is [2^31, 2^32 - 1].
|
||||
HardenedKeyStart = 0x80000000 // 2^31
|
||||
|
||||
// MinSeedBytes is the minimum number of bytes allowed for a seed to
|
||||
// a master node.
|
||||
MinSeedBytes = 16 // 128 bits
|
||||
|
||||
// MaxSeedBytes is the maximum number of bytes allowed for a seed to
|
||||
// a master node.
|
||||
MaxSeedBytes = 64 // 512 bits
|
||||
|
||||
// serializedKeyLen is the length of a serialized public or private
|
||||
// extended key. It consists of 4 bytes version, 1 byte depth, 4 bytes
|
||||
// fingerprint, 4 bytes child number, 32 bytes chain code, and 33 bytes
|
||||
// public/private key data.
|
||||
serializedKeyLen = 4 + 1 + 4 + 4 + 32 + 33 // 78 bytes
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrDeriveHardFromPublic describes an error in which the caller
|
||||
// attempted to derive a hardened extended key from a public key.
|
||||
ErrDeriveHardFromPublic = errors.New("cannot derive a hardened key " +
|
||||
"from a public key")
|
||||
|
||||
// ErrNotPrivExtKey describes an error in which the caller attempted
|
||||
// to extract a private key from a public extended key.
|
||||
ErrNotPrivExtKey = errors.New("unable to create private keys from a " +
|
||||
"public extended key")
|
||||
|
||||
// ErrInvalidChild describes an error in which the child at a specific
|
||||
// index is invalid due to the derived key falling outside of the valid
|
||||
// range for secp256k1 private keys. This error indicates the caller
|
||||
// should simply ignore the invalid child extended key at this index and
|
||||
// increment to the next index.
|
||||
ErrInvalidChild = errors.New("the extended key at this index is invalid")
|
||||
|
||||
// ErrUnusableSeed describes an error in which the provided seed is not
|
||||
// usable due to the derived key falling outside of the valid range for
|
||||
// secp256k1 private keys. This error indicates the caller must choose
|
||||
// another seed.
|
||||
ErrUnusableSeed = errors.New("unusable seed")
|
||||
|
||||
// ErrInvalidSeedLen describes an error in which the provided seed or
|
||||
// seed length is not in the allowed range.
|
||||
ErrInvalidSeedLen = fmt.Errorf("seed length must be between %d and %d "+
|
||||
"bits", MinSeedBytes*8, MaxSeedBytes*8)
|
||||
|
||||
// ErrBadChecksum describes an error in which the checksum encoded with
|
||||
// a serialized extended key does not match the calculated value.
|
||||
ErrBadChecksum = errors.New("bad extended key checksum")
|
||||
|
||||
// ErrInvalidKeyLen describes an error in which the provided serialized
|
||||
// key is not the expected length.
|
||||
ErrInvalidKeyLen = errors.New("the provided serialized extended key " +
|
||||
"length is invalid")
|
||||
)
|
||||
|
||||
// masterKey is the master key used along with a random seed used to generate
|
||||
// the master node in the hierarchical tree.
|
||||
var masterKey = []byte("Bitcoin seed")
|
||||
|
||||
// ExtendedKey houses all the information needed to support a hierarchical
|
||||
// deterministic extended key. See the package overview documentation for
|
||||
// more details on how to use extended keys.
|
||||
type ExtendedKey struct {
|
||||
key []byte // This will be the pubkey for extended pub keys
|
||||
pubKey []byte // This will only be set for extended priv keys
|
||||
chainCode []byte
|
||||
depth uint16
|
||||
parentFP []byte
|
||||
childNum uint32
|
||||
version []byte
|
||||
isPrivate bool
|
||||
}
|
||||
|
||||
// newExtendedKey returns a new instance of an extended key with the given
|
||||
// fields. No error checking is performed here as it's only intended to be a
|
||||
// convenience method used to create a populated struct.
|
||||
func newExtendedKey(version, key, chainCode, parentFP []byte, depth uint16,
|
||||
childNum uint32, isPrivate bool) *ExtendedKey {
|
||||
|
||||
// NOTE: The pubKey field is intentionally left nil so it is only
|
||||
// computed and memoized as required.
|
||||
return &ExtendedKey{
|
||||
key: key,
|
||||
chainCode: chainCode,
|
||||
depth: depth,
|
||||
parentFP: parentFP,
|
||||
childNum: childNum,
|
||||
version: version,
|
||||
isPrivate: isPrivate,
|
||||
}
|
||||
}
|
||||
|
||||
// pubKeyBytes returns bytes for the serialized compressed public key associated
|
||||
// with this extended key in an efficient manner including memoization as
|
||||
// necessary.
|
||||
//
|
||||
// When the extended key is already a public key, the key is simply returned as
|
||||
// is since it's already in the correct form. However, when the extended key is
|
||||
// a private key, the public key will be calculated and memoized so future
|
||||
// accesses can simply return the cached result.
|
||||
func (k *ExtendedKey) pubKeyBytes() []byte {
|
||||
// Just return the key if it's already an extended public key.
|
||||
if !k.isPrivate {
|
||||
return k.key
|
||||
}
|
||||
|
||||
// This is a private extended key, so calculate and memoize the public
|
||||
// key if needed.
|
||||
if len(k.pubKey) == 0 {
|
||||
pkx, pky := chainec.Secp256k1.ScalarBaseMult(k.key)
|
||||
pubKey := chainec.Secp256k1.NewPublicKey(pkx, pky)
|
||||
k.pubKey = pubKey.SerializeCompressed()
|
||||
}
|
||||
|
||||
return k.pubKey
|
||||
}
|
||||
|
||||
// IsPrivate returns whether or not the extended key is a private extended key.
|
||||
//
|
||||
// A private extended key can be used to derive both hardened and non-hardened
|
||||
// child private and public extended keys. A public extended key can only be
|
||||
// used to derive non-hardened child public extended keys.
|
||||
func (k *ExtendedKey) IsPrivate() bool {
|
||||
return k.isPrivate
|
||||
}
|
||||
|
||||
// ParentFingerprint returns a fingerprint of the parent extended key from which
|
||||
// this one was derived.
|
||||
func (k *ExtendedKey) ParentFingerprint() uint32 {
|
||||
return binary.BigEndian.Uint32(k.parentFP)
|
||||
}
|
||||
|
||||
// Child returns a derived child extended key at the given index. When this
|
||||
// extended key is a private extended key (as determined by the IsPrivate
|
||||
// function), a private extended key will be derived. Otherwise, the derived
|
||||
// extended key will be also be a public extended key.
|
||||
//
|
||||
// When the index is greater to or equal than the HardenedKeyStart constant, the
|
||||
// derived extended key will be a hardened extended key. It is only possible to
|
||||
// derive a hardended extended key from a private extended key. Consequently,
|
||||
// this function will return ErrDeriveHardFromPublic if a hardened child
|
||||
// extended key is requested from a public extended key.
|
||||
//
|
||||
// A hardened extended key is useful since, as previously mentioned, it requires
|
||||
// a parent private extended key to derive. In other words, normal child
|
||||
// extended public keys can be derived from a parent public extended key (no
|
||||
// knowledge of the parent private key) whereas hardened extended keys may not
|
||||
// be.
|
||||
//
|
||||
// NOTE: There is an extremely small chance (< 1 in 2^127) the specific child
|
||||
// index does not derive to a usable child. The ErrInvalidChild error will be
|
||||
// returned if this should occur, and the caller is expected to ignore the
|
||||
// invalid child and simply increment to the next index.
|
||||
func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) {
|
||||
// There are four scenarios that could happen here:
|
||||
// 1) Private extended key -> Hardened child private extended key
|
||||
// 2) Private extended key -> Non-hardened child private extended key
|
||||
// 3) Public extended key -> Non-hardened child public extended key
|
||||
// 4) Public extended key -> Hardened child public extended key (INVALID!)
|
||||
|
||||
// Case #4 is invalid, so error out early.
|
||||
// A hardened child extended key may not be created from a public
|
||||
// extended key.
|
||||
isChildHardened := i >= HardenedKeyStart
|
||||
if !k.isPrivate && isChildHardened {
|
||||
return nil, ErrDeriveHardFromPublic
|
||||
}
|
||||
|
||||
// The data used to derive the child key depends on whether or not the
|
||||
// child is hardened per [BIP32].
|
||||
//
|
||||
// For hardened children:
|
||||
// 0x00 || ser256(parentKey) || ser32(i)
|
||||
//
|
||||
// For normal children:
|
||||
// serP(parentPubKey) || ser32(i)
|
||||
keyLen := 33
|
||||
data := make([]byte, keyLen+4)
|
||||
if isChildHardened {
|
||||
// Case #1.
|
||||
// When the child is a hardened child, the key is known to be a
|
||||
// private key due to the above early return. Pad it with a
|
||||
// leading zero as required by [BIP32] for deriving the child.
|
||||
copy(data[1:], k.key)
|
||||
} else {
|
||||
// Case #2 or #3.
|
||||
// This is either a public or private extended key, but in
|
||||
// either case, the data which is used to derive the child key
|
||||
// starts with the secp256k1 compressed public key bytes.
|
||||
copy(data, k.pubKeyBytes())
|
||||
}
|
||||
binary.BigEndian.PutUint32(data[keyLen:], i)
|
||||
|
||||
// Take the HMAC-SHA512 of the current key's chain code and the derived
|
||||
// data:
|
||||
// I = HMAC-SHA512(Key = chainCode, Data = data)
|
||||
hmac512 := hmac.New(sha512.New, k.chainCode)
|
||||
hmac512.Write(data)
|
||||
ilr := hmac512.Sum(nil)
|
||||
|
||||
// Split "I" into two 32-byte sequences Il and Ir where:
|
||||
// Il = intermediate key used to derive the child
|
||||
// Ir = child chain code
|
||||
il := ilr[:len(ilr)/2]
|
||||
childChainCode := ilr[len(ilr)/2:]
|
||||
|
||||
// Both derived public or private keys rely on treating the left 32-byte
|
||||
// sequence calculated above (Il) as a 256-bit integer that must be
|
||||
// within the valid range for a secp256k1 private key. There is a small
|
||||
// chance (< 1 in 2^127) this condition will not hold, and in that case,
|
||||
// a child extended key can't be created for this index and the caller
|
||||
// should simply increment to the next index.
|
||||
ilNum := new(big.Int).SetBytes(il)
|
||||
if ilNum.Cmp(chainec.Secp256k1.GetN()) >= 0 || ilNum.Sign() == 0 {
|
||||
return nil, ErrInvalidChild
|
||||
}
|
||||
|
||||
// The algorithm used to derive the child key depends on whether or not
|
||||
// a private or public child is being derived.
|
||||
//
|
||||
// For private children:
|
||||
// childKey = parse256(Il) + parentKey
|
||||
//
|
||||
// For public children:
|
||||
// childKey = serP(point(parse256(Il)) + parentKey)
|
||||
var isPrivate bool
|
||||
var childKey []byte
|
||||
if k.isPrivate {
|
||||
// Case #1 or #2.
|
||||
// Add the parent private key to the intermediate private key to
|
||||
// derive the final child key.
|
||||
//
|
||||
// childKey = parse256(Il) + parenKey
|
||||
keyNum := new(big.Int).SetBytes(k.key)
|
||||
ilNum.Add(ilNum, keyNum)
|
||||
ilNum.Mod(ilNum, chainec.Secp256k1.GetN())
|
||||
childKey = ilNum.Bytes()
|
||||
isPrivate = true
|
||||
} else {
|
||||
// Case #3.
|
||||
// Calculate the corresponding intermediate public key for
|
||||
// intermediate private key.
|
||||
ilx, ily := chainec.Secp256k1.ScalarBaseMult(il)
|
||||
if ilx.Sign() == 0 || ily.Sign() == 0 {
|
||||
return nil, ErrInvalidChild
|
||||
}
|
||||
|
||||
// Convert the serialized compressed parent public key into X
|
||||
// and Y coordinates so it can be added to the intermediate
|
||||
// public key.
|
||||
pubKey, err := chainec.Secp256k1.ParsePubKey(k.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the intermediate public key to the parent public key to
|
||||
// derive the final child key.
|
||||
//
|
||||
// childKey = serP(point(parse256(Il)) + parentKey)
|
||||
childX, childY := chainec.Secp256k1.Add(ilx, ily, pubKey.GetX(),
|
||||
pubKey.GetY())
|
||||
pk := chainec.Secp256k1.NewPublicKey(childX, childY)
|
||||
childKey = pk.SerializeCompressed()
|
||||
}
|
||||
|
||||
// The fingerprint of the parent for the derived child is the first 4
|
||||
// bytes of the RIPEMD160(SHA256(parentPubKey)).
|
||||
parentFP := dcrutil.Hash160(k.pubKeyBytes())[:4]
|
||||
return newExtendedKey(k.version, childKey, childChainCode, parentFP,
|
||||
k.depth+1, i, isPrivate), nil
|
||||
}
|
||||
|
||||
// Neuter returns a new extended public key from this extended private key. The
|
||||
// same extended key will be returned unaltered if it is already an extended
|
||||
// public key.
|
||||
//
|
||||
// As the name implies, an extended public key does not have access to the
|
||||
// private key, so it is not capable of signing transactions or deriving
|
||||
// child extended private keys. However, it is capable of deriving further
|
||||
// child extended public keys.
|
||||
func (k *ExtendedKey) Neuter() (*ExtendedKey, error) {
|
||||
// Already an extended public key.
|
||||
if !k.isPrivate {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
// Get the associated public extended key version bytes.
|
||||
version, err := chaincfg.HDPrivateKeyToPublicKeyID(k.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert it to an extended public key. The key for the new extended
|
||||
// key will simply be the pubkey of the current extended private key.
|
||||
//
|
||||
// This is the function N((k,c)) -> (K, c) from [BIP32].
|
||||
return newExtendedKey(version, k.pubKeyBytes(), k.chainCode, k.parentFP,
|
||||
k.depth, k.childNum, false), nil
|
||||
}
|
||||
|
||||
// ECPubKey converts the extended key to a dcrec public key and returns it.
|
||||
func (k *ExtendedKey) ECPubKey() (chainec.PublicKey, error) {
|
||||
return chainec.Secp256k1.ParsePubKey(k.pubKeyBytes())
|
||||
}
|
||||
|
||||
// ECPrivKey converts the extended key to a dcrec private key and returns it.
|
||||
// As you might imagine this is only possible if the extended key is a private
|
||||
// extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey
|
||||
// error will be returned if this function is called on a public extended key.
|
||||
func (k *ExtendedKey) ECPrivKey() (chainec.PrivateKey, error) {
|
||||
if !k.isPrivate {
|
||||
return nil, ErrNotPrivExtKey
|
||||
}
|
||||
|
||||
privKey, _ := chainec.Secp256k1.PrivKeyFromBytes(k.key)
|
||||
return privKey, nil
|
||||
}
|
||||
|
||||
// Address converts the extended key to a standard decred pay-to-pubkey-hash
|
||||
// address for the passed network.
|
||||
func (k *ExtendedKey) Address(net *chaincfg.Params) (*dcrutil.AddressPubKeyHash, error) {
|
||||
pkHash := dcrutil.Hash160(k.pubKeyBytes())
|
||||
return dcrutil.NewAddressPubKeyHash(pkHash, net, chainec.ECTypeSecp256k1)
|
||||
}
|
||||
|
||||
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
||||
// If the length of the source is smaller than the passed size, leading zero
|
||||
// bytes are appended to the dst slice before appending src.
|
||||
func paddedAppend(size uint, dst, src []byte) []byte {
|
||||
for i := 0; i < int(size)-len(src); i++ {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
return append(dst, src...)
|
||||
}
|
||||
|
||||
// String returns the extended key as a human-readable base58-encoded string.
|
||||
func (k *ExtendedKey) String() (string, error) {
|
||||
if len(k.key) == 0 {
|
||||
return "", fmt.Errorf("zeroed extended key")
|
||||
}
|
||||
|
||||
var childNumBytes [4]byte
|
||||
depthByte := byte(k.depth % 256)
|
||||
binary.BigEndian.PutUint32(childNumBytes[:], k.childNum)
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
serializedBytes := make([]byte, 0, serializedKeyLen+4)
|
||||
serializedBytes = append(serializedBytes, k.version...)
|
||||
serializedBytes = append(serializedBytes, depthByte)
|
||||
serializedBytes = append(serializedBytes, k.parentFP...)
|
||||
serializedBytes = append(serializedBytes, childNumBytes[:]...)
|
||||
serializedBytes = append(serializedBytes, k.chainCode...)
|
||||
if k.isPrivate {
|
||||
serializedBytes = append(serializedBytes, 0x00)
|
||||
serializedBytes = paddedAppend(32, serializedBytes, k.key)
|
||||
} else {
|
||||
serializedBytes = append(serializedBytes, k.pubKeyBytes()...)
|
||||
}
|
||||
|
||||
checkSum := chainhash.HashB(chainhash.HashB(serializedBytes))[:4]
|
||||
serializedBytes = append(serializedBytes, checkSum...)
|
||||
return base58.Encode(serializedBytes), nil
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the extended key is associated with the
|
||||
// passed decred network.
|
||||
func (k *ExtendedKey) IsForNet(net *chaincfg.Params) bool {
|
||||
return bytes.Equal(k.version, net.HDPrivateKeyID[:]) ||
|
||||
bytes.Equal(k.version, net.HDPublicKeyID[:])
|
||||
}
|
||||
|
||||
// SetNet associates the extended key, and any child keys yet to be derived from
|
||||
// it, with the passed network.
|
||||
func (k *ExtendedKey) SetNet(net *chaincfg.Params) {
|
||||
if k.isPrivate {
|
||||
k.version = net.HDPrivateKeyID[:]
|
||||
} else {
|
||||
k.version = net.HDPublicKeyID[:]
|
||||
}
|
||||
}
|
||||
|
||||
// zero sets all bytes in the passed slice to zero. This is used to
|
||||
// explicitly clear private key material from memory.
|
||||
func zero(b []byte) {
|
||||
lenb := len(b)
|
||||
for i := 0; i < lenb; i++ {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Zero manually clears all fields and bytes in the extended key. This can be
|
||||
// used to explicitly clear key material from memory for enhanced security
|
||||
// against memory scraping. This function only clears this particular key and
|
||||
// not any children that have already been derived.
|
||||
func (k *ExtendedKey) Zero() {
|
||||
zero(k.key)
|
||||
zero(k.pubKey)
|
||||
zero(k.chainCode)
|
||||
zero(k.parentFP)
|
||||
k.version = nil
|
||||
k.key = nil
|
||||
k.depth = 0
|
||||
k.childNum = 0
|
||||
k.isPrivate = false
|
||||
}
|
||||
|
||||
// NewMaster creates a new master node for use in creating a hierarchical
|
||||
// deterministic key chain. The seed must be between 128 and 512 bits and
|
||||
// should be generated by a cryptographically secure random generation source.
|
||||
//
|
||||
// NOTE: There is an extremely small chance (< 1 in 2^127) the provided seed
|
||||
// will derive to an unusable secret key. The ErrUnusable error will be
|
||||
// returned if this should occur, so the caller must check for it and generate a
|
||||
// new seed accordingly.
|
||||
func NewMaster(seed []byte, net *chaincfg.Params) (*ExtendedKey, error) {
|
||||
// Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes].
|
||||
if len(seed) < MinSeedBytes || len(seed) > MaxSeedBytes {
|
||||
return nil, ErrInvalidSeedLen
|
||||
}
|
||||
|
||||
// First take the HMAC-SHA512 of the master key and the seed data:
|
||||
// I = HMAC-SHA512(Key = "Bitcoin seed", Data = S)
|
||||
hmac512 := hmac.New(sha512.New, masterKey)
|
||||
hmac512.Write(seed)
|
||||
lr := hmac512.Sum(nil)
|
||||
|
||||
// Split "I" into two 32-byte sequences Il and Ir where:
|
||||
// Il = master secret key
|
||||
// Ir = master chain code
|
||||
secretKey := lr[:len(lr)/2]
|
||||
chainCode := lr[len(lr)/2:]
|
||||
|
||||
// Ensure the key in usable.
|
||||
secretKeyNum := new(big.Int).SetBytes(secretKey)
|
||||
if secretKeyNum.Cmp(chainec.Secp256k1.GetN()) >= 0 ||
|
||||
secretKeyNum.Sign() == 0 {
|
||||
return nil, ErrUnusableSeed
|
||||
}
|
||||
|
||||
parentFP := []byte{0x00, 0x00, 0x00, 0x00}
|
||||
return newExtendedKey(net.HDPrivateKeyID[:], secretKey, chainCode,
|
||||
parentFP, 0, 0, true), nil
|
||||
}
|
||||
|
||||
// NewKeyFromString returns a new extended key instance from a base58-encoded
|
||||
// extended key.
|
||||
func NewKeyFromString(key string) (*ExtendedKey, error) {
|
||||
// The base58-decoded extended key must consist of a serialized payload
|
||||
// plus an additional 4 bytes for the checksum.
|
||||
decoded := base58.Decode(key)
|
||||
if len(decoded) != serializedKeyLen+4 {
|
||||
return nil, ErrInvalidKeyLen
|
||||
}
|
||||
|
||||
// The serialized format is:
|
||||
// version (4) || depth (1) || parent fingerprint (4)) ||
|
||||
// child num (4) || chain code (32) || key data (33) || checksum (4)
|
||||
|
||||
// Split the payload and checksum up and ensure the checksum matches.
|
||||
payload := decoded[:len(decoded)-4]
|
||||
checkSum := decoded[len(decoded)-4:]
|
||||
expectedCheckSum := chainhash.HashB(chainhash.HashB(payload))[:4]
|
||||
if !bytes.Equal(checkSum, expectedCheckSum) {
|
||||
return nil, ErrBadChecksum
|
||||
}
|
||||
|
||||
// Deserialize each of the payload fields.
|
||||
version := payload[:4]
|
||||
depth := uint16(payload[4:5][0])
|
||||
parentFP := payload[5:9]
|
||||
childNum := binary.BigEndian.Uint32(payload[9:13])
|
||||
chainCode := payload[13:45]
|
||||
keyData := payload[45:78]
|
||||
|
||||
// The key data is a private key if it starts with 0x00. Serialized
|
||||
// compressed pubkeys either start with 0x02 or 0x03.
|
||||
isPrivate := keyData[0] == 0x00
|
||||
if isPrivate {
|
||||
// Ensure the private key is valid. It must be within the range
|
||||
// of the order of the secp256k1 curve and not be 0.
|
||||
keyData = keyData[1:]
|
||||
keyNum := new(big.Int).SetBytes(keyData)
|
||||
if keyNum.Cmp(chainec.Secp256k1.GetN()) >= 0 || keyNum.Sign() == 0 {
|
||||
return nil, ErrUnusableSeed
|
||||
}
|
||||
} else {
|
||||
// Ensure the public key parses correctly and is actually on the
|
||||
// secp256k1 curve.
|
||||
_, err := chainec.Secp256k1.ParsePubKey(keyData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return newExtendedKey(version, keyData, chainCode, parentFP, depth,
|
||||
childNum, isPrivate), nil
|
||||
}
|
||||
|
||||
// GenerateSeed returns a cryptographically secure random seed that can be used
|
||||
// as the input for the NewMaster function to generate a new master node.
|
||||
//
|
||||
// The length is in bytes and it must be between 16 and 64 (128 to 512 bits).
|
||||
// The recommended length is 32 (256 bits) as defined by the RecommendedSeedLen
|
||||
// constant.
|
||||
func GenerateSeed(length uint8) ([]byte, error) {
|
||||
// Per [BIP32], the seed must be in range [MinSeedBytes, MaxSeedBytes].
|
||||
if length < MinSeedBytes || length > MaxSeedBytes {
|
||||
return nil, ErrInvalidSeedLen
|
||||
}
|
||||
|
||||
buf := make([]byte, length)
|
||||
_, err := rand.Read(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
982
dcrutil/hdkeychain/extendedkey_test.go
Normal file
982
dcrutil/hdkeychain/extendedkey_test.go
Normal file
@ -0,0 +1,982 @@
|
||||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hdkeychain_test
|
||||
|
||||
// References:
|
||||
// [BIP32]: BIP0032 - Hierarchical Deterministic Wallets
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/dcrutil/hdkeychain"
|
||||
)
|
||||
|
||||
// TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the
|
||||
// derivation works as intended.
|
||||
func TestBIP0032Vectors(t *testing.T) {
|
||||
// The master seeds for each of the two test vectors in [BIP32].
|
||||
testVec1MasterHex := "000102030405060708090a0b0c0d0e0f"
|
||||
testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
|
||||
hkStart := uint32(0x80000000)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
master string
|
||||
path []uint32
|
||||
wantPub string
|
||||
wantPriv string
|
||||
net *chaincfg.Params
|
||||
}{
|
||||
// Test vector 1
|
||||
{
|
||||
name: "test vector 1 chain m",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{},
|
||||
wantPub: "dpubZ9169KDAEUnyoBhjjmT2VaEodr6pUTDoqCEAeqgbfr2JfkB88BbK77jbTYbcYXb2FVz7DKBdW4P618yd51MwF8DjKVopSbS7Lkgi6bowX5w",
|
||||
wantPriv: "dprv3hCznBesA6jBtmoyVFPfyMSZ1qYZ3WdjdebquvkEfmRfxC9VFEFi2YDaJqHnx7uGe75eGSa3Mn3oHK11hBW7KZUrPxwbCPBmuCi1nwm182s",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart},
|
||||
wantPub: "dpubZCGVaKZBiMo7pMgLaZm1qmchjWenTeVcUdFQkTNsFGFEA6xs4EW8PKiqYqP7HBAitt9Hw16VQkQ1tjsZQSHNWFc6bEK6bLqrbco24FzBTY4",
|
||||
wantPriv: "dprv3kUQDBztdyjKuwnaL3hfKYpT7W6X2huYH5d61YSWFBebSYwEBHAXJkCpQ7rvMAxPzKqxVCGLvBqWvGxXjAyMJsV1XwKkfnQCM9KctC8k8bk",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1},
|
||||
wantPub: "dpubZEDyZgdnFBMHxqNhfCUwBfAg1UmXHiTmB5jKtzbAZhF8PTzy2PwAicNdkg1CmW6TARxQeUbgC7nAQenJts4YoG3KMiqcjsjgeMvwLc43w6C",
|
||||
wantPriv: "dprv3nRtCZ5VAoHW4RUwQgRafSNRPUDFrmsgyY71A5eoZceVfuyL9SbZe2rcbwDW2UwpkEniE4urffgbypegscNchPajWzy9QS4cRxF8QYXsZtq",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2},
|
||||
wantPub: "dpubZGLz7gsJAWzUksvtw3opxx5eeLq5fRaUMDABA3bdUVfnGUk5fiS5Cc3kZGTjWtYr3jrEavQQnAF6jv2WCpZtFX4uFgifXqev6ED1TM9rTCB",
|
||||
wantPriv: "dprv3pYtkZK168vgrU38gXkUSjHQ2LGpEUzQ9fXrR8fGUR59YviSnm6U82XjQYhpJEUPnVcC9bguJBQU5xVM4VFcDHu9BgScGPA6mQMH4bn5Cth",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H/2",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2, 2},
|
||||
wantPub: "dpubZHv6Cfp2XRSWHQXZBo1dLmVM421Zdkc4MePkyBXCLFttVkCmwZkxth4ZV9PzkFP3DtD5xcVq2CPSYpJMWMaoxu1ixz4GNZFVcE2xnHP6chJ",
|
||||
wantPriv: "dprv3r7zqYFjT3NiNzdnwGxGpYh6S1TJCp1zA6mSEGaqLBJFnCB94cRMp7YYLR49aTZHZ7ya1CXwQJ6rodKeU9NgQTxkPSK7pzgZRgjYkQ7rgJh",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H/2/1000000000",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000},
|
||||
wantPub: "dpubZL6d9amjfRy1zeoZM2zHDU7uoMvwPqtxHRQAiJjeEtQQWjP3retQV1qKJyzUd6ZJNgbJGXjtc5pdoBcTTYTLoxQzvV9JJCzCjB2eCWpRf8T",
|
||||
wantPriv: "dprv3tJXnTDSb3uE6Euo6WvvhFKfBMNfxuJt5smqyPoHEoomoBMQyhYoQSKJAHWtWxmuqdUVb8q9J2NaTkF6rYm6XDrSotkJ55bM21fffa7VV97",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
|
||||
// Test vector 2
|
||||
{
|
||||
name: "test vector 2 chain m",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{},
|
||||
wantPub: "dpubZ9169KDAEUnynoD4qvXJwmxZt3FFA5UdWn1twnRReE9AxjCKJLNFY1uBoegbFmwzA4Du7yqnu8tLivhrCCH6P3DgBS1HH5vmf8MpNXvvYT9",
|
||||
wantPriv: "dprv3hCznBesA6jBtPKJbQTxRZAKG2gyj8tZKEPaCsV4e9YYFBAgRP2eTSPAeu4r8dTMt9q51j2Vdt5zNqj7jbtovvocrP1qLj6WUTLF9xYQt4y",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{0},
|
||||
wantPub: "dpubZBA4RCkCybJFaNbqPuBiyfXY1rvmG1XTdCy1AY1U96dxkFqWc2i5KREMh7NYPpy7ZPMhdpFMAesex3JdFDfX4J5FEW3HjSacqEYPfwb9Cj7",
|
||||
wantPriv: "dprv3jMy45BuuDETfxi59P8NTSjHPrNVq4wPRfLgRd57923L2hosj5NUEqiLYQ4i7fJtUpiXZLr2wUeToJY2Tm5sCpAJdajEHDmieVJiPQNXwu9",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647H",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{0, hkStart + 2147483647},
|
||||
wantPub: "dpubZDUNkZEcCRCZEizDGL9sAQbZRKSnaxQLeqN9zpueeqCyq2VY7NUGMXASacsK96S8XzNjq3YgFgwLtj8MJBToW6To9U5zxuazEyh89bjR1xA",
|
||||
wantPriv: "dprv3mgHPRgK838mLK6T1p6WeBoJoJtXA1pGTHjqFuyHekcM7UTuER8fGweRRsoLqSuHa98uskVPnJnfWZEBUC1AVmXnSCPDvUFKydXNnnPHTuQ",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647H/1",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{0, hkStart + 2147483647, 1},
|
||||
wantPub: "dpubZF3wJh7SfggGg74QZW3EE9ei8uQSJEFgd62uyuK5iMgQzUNjpSnprgTpYz3d6Q3fXXtEEXQqpzWcP4LUVuXFsgA8JKt1Hot5kyUk4pPRhDz",
|
||||
wantPriv: "dprv3oFqwZZ9bJcUmhAeJyyshvrTWtrAsHfcRYQbEzNiiH5nGvM6wVTDn6woQEz92b2EHTYZBtLi82jKEnxSouA3cVaW8YWBsw5c3f4mwAhA3d2",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647H/1/2147483646H",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646},
|
||||
wantPub: "dpubZH38NEg1CW19dGZs8NdaT4hDkz7wXPstio1mGpHSAXHpSGW3UnTrn25ERT1Mp8ae5GMoQHMbgQiPrChMXQMdx3UqS8YqFkT1pqait8fY92u",
|
||||
wantPriv: "dprv3qF3177i87wMirg6sraDvqty8yZg6THpXFPSXuM5AShBiiUQbq8FhSZDGkYmBNR3RKfBrxzkKDBpsRFJfTnQfLsvpPPqRnakat6hHQA43X9",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2",
|
||||
master: testVec2MasterHex,
|
||||
path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2},
|
||||
wantPub: "dpubZJoBFoQJ35zvEBgsfhJBssnAp8TY5gvruzQFLmyxcqRb7enVtGfSkLo2CkAZJMpa6T2fx6fUtvTgXtUvSVgAZ56bEwGxQsToeZfFV8VadE1",
|
||||
wantPriv: "dprv3s15tfqzxhw8Kmo7RBEqMeyvC7uGekLniSmvbs3bckpxQ6ks1KKqfmH144Jgh3PLxkyZRcS367kp7DrtUmnG16NpnsoNhxSXRgKbJJ7MUQR",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
|
||||
// Test vector 1 - Testnet
|
||||
{
|
||||
name: "test vector 1 chain m - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{},
|
||||
wantPub: "tpubVhnMyQmZAhoosedBTX7oacwyCNc5qtdEMoNHudUCW1R6WZTvqCZQoNJHSn4H11puwdk4qyDv2ET637EDap4r8HH3odjBC5nEjmnPcsDfLwm",
|
||||
wantPriv: "tprvZUo1ZuEfLLFWfAYiMVaoDV1EeLmbSRuNzaSh7F4awft7dm8nHfFAFZyobWQyV8Qr26r8M2CmNw6nEb35HaECWFGy1vzx2ZGdyfBeaaHudoi",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart},
|
||||
wantPub: "tpubVm3mQR7aeaowtpbnJKRnvpKsJ3A3q5u31EPY1FAU5Re1zvFfmFUE5aHXY4qmjfQcb1uFZf8mvvU1vi89vEzHPQfR5NETLqByzdthaYfQGja",
|
||||
wantPriv: "tprvZY4QzuagpDFegLXKCHtnZgP8k1KZRdBBe1TwCrkrX67387vXDi9yXmy3gnz6tBTyNKcSZmu4wLtVsYzbKZhSVZH89uP7VxV4RboFfozTBMQ",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1 - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1},
|
||||
wantPub: "tpubVo1FPnCBBQN83JJ9Nx9iGhsqa1Gnf9sBhgsT9nNmPrdvEHHmjQuGQrwKjuTsDzLLrZiNH8dxiHrASd2uQfmTgR6dqrkyVN5p3P2crvfgpEQ",
|
||||
wantPriv: "tprvZa1tzGfHM2opppDgGvchuZw71ySJFh9LLTwrMPy9qX6wMUxdBsb1s4cqtcLgZVTQ8EZCJeYagpjaw6gkU16ht5Nr8y2WEc9UWQimC4Y6MFs",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2},
|
||||
wantPub: "tpubVq8FwnRh6k1JqLrLeoUc3znpCsLM2rytspJJQqPEJf4a7J2tNjQAtrcSYVvPyNnjjscCDaShJLK6mtH6idGo8g8Djpe2HL13VFJgygvRmc9",
|
||||
wantPriv: "tprvZc8uYGtoGNT1crmsYmwbgrr5eqVrdQG3WbNhcSyckKXbEVhjqC5vM4HxhDpzqEyyAVNgEBKdKLTT3EXQesyhPyhFoeVy6ZExqrpurCCRvrF",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H/2 - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2, 2},
|
||||
wantPub: "tpubVrhN2mNRTeTLMsSzuYgQRpCWcYWq1C1UtFXtDyJoARHgLZVaeaj4awdFUNrfCjcvv1y3bGY7YNTSanYx2AHir453T7yd83bd1F8eJgtdq3F",
|
||||
wantPriv: "tprvZdi1dFqXdGu39PNXoX9Q4gFn4WgLbjHdX2cHRauBc5khTmAS73Qp39Jmd6BL7U4rw7k45nAfRT9qkuMi4Y6mb9ks1QNUfAmRW9DBY5g5ELT",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0H/1/2H/2/1000000000 - testnet",
|
||||
master: testVec1MasterHex,
|
||||
path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000},
|
||||
wantPub: "tpubVtstygL8beyr57j14nf4JWq5MtSCmHJNp2YHy6XF53oCMYfrZfrWBGQ1JDT95aoC4pMFuBnB8Ftdq9s3yMAFh7UKQd4f3hLL8C8KipwSek6",
|
||||
wantPriv: "tprvZftYaAoEmHRYrdeXxm83wNtLorbiMpaXSochAi7dWiGDUkLi28YFdU5XSxe53yHVDdEyfiTsKBRZR2HASwVBhueZRroeuFgD6U9JTC1mUyU",
|
||||
net: &chaincfg.TestNet2Params,
|
||||
},
|
||||
}
|
||||
|
||||
tests:
|
||||
for i, test := range tests {
|
||||
masterSeed, err := hex.DecodeString(test.master)
|
||||
if err != nil {
|
||||
t.Errorf("DecodeString #%d (%s): unexpected error: %v",
|
||||
i, test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
extKey, err := hdkeychain.NewMaster(masterSeed, test.net)
|
||||
if err != nil {
|
||||
t.Errorf("NewMaster #%d (%s): unexpected error when "+
|
||||
"creating new master key: %v", i, test.name,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, childNum := range test.path {
|
||||
var err error
|
||||
extKey, err = extKey.Child(childNum)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
continue tests
|
||||
}
|
||||
}
|
||||
|
||||
privStr, _ := extKey.String()
|
||||
if privStr != test.wantPriv {
|
||||
t.Errorf("Serialize #%d (%s): mismatched serialized "+
|
||||
"private extended key -- got: %s, want: %s", i,
|
||||
test.name, privStr, test.wantPriv)
|
||||
continue
|
||||
}
|
||||
|
||||
pubKey, err := extKey.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter #%d (%s): unexpected error: %v ", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Neutering a second time should have no effect.
|
||||
pubKey, err = pubKey.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
|
||||
test.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
pubStr, _ := pubKey.String()
|
||||
if pubStr != test.wantPub {
|
||||
t.Errorf("Neuter #%d (%s): mismatched serialized "+
|
||||
"public extended key -- got: %s, want: %s", i,
|
||||
test.name, pubStr, test.wantPub)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrivateDerivation tests several vectors which derive private keys from
|
||||
// other private keys works as intended.
|
||||
func TestPrivateDerivation(t *testing.T) {
|
||||
// The private extended keys for test vectors in [BIP32].
|
||||
testVec1MasterPrivKey := "dprv3hCznBesA6jBucms1ZhyGeFfvJfBSwfs7ZFrxS8tdYzbjDZe2UwSaL7EbYo1qa88DmtyyG5cL9tdGxHkD89JmeZTbz5sVYU4Dgtijiio4Sc"
|
||||
testVec2MasterPrivKey := "dprv3hCznBesA6jBtPKJbQTxRZAKG2gyj8tZKEPaCsV4e9YYFBAgRP2eTSPAeu4r8dTMt9q51j2Vdt5zNqj7jbtovvocrP1qLj6WUTLF9xYQt4y"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
master string
|
||||
path []uint32
|
||||
wantPriv string
|
||||
}{
|
||||
// Test vector 1
|
||||
{
|
||||
name: "test vector 1 chain m",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{},
|
||||
wantPriv: "dprv3hCznBesA6jBucms1ZhyGeFfvJfBSwfs7ZFrxS8tdYzbjDZe2UwSaL7EbYo1qa88DmtyyG5cL9tdGxHkD89JmeZTbz5sVYU4Dgtijiio4Sc",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{0},
|
||||
wantPriv: "dprv3jFfEhxvVxy6NJWopujhfg7syQL71xCRgNoGUpQTtjTpCwzigwtCwssQGbRQsby7PBs1Yp8Wu7isu396qeNof13EZuxbCTJVF1xkoFAQHWj",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{0, 1},
|
||||
wantPriv: "dprv3mWLns1v1fdLhxStaJHh3BqxmTi14RHHeWdNU6oU8sSkTDmAr54yK6La2APy3rAZr9ZJAdm5asTJaqBZ3vBYVSPHqyL8kbcCp5jgqfxBs4x",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{0, 1, 2},
|
||||
wantPriv: "dprv3oDxSziXR1rQVWwWWBRKgCQU3vN6dnR3ekzHzvZRdgfVvrYSE35saJh8UdfSxCgtMn7pnbeMXWbyBbwxoncC9LMrnuH1AoJSB259c4XgmnN",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2/2",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{0, 1, 2, 2},
|
||||
wantPriv: "dprv3rYHNih25i8MqeRjhFq8mLnK4a3J63a9zkYTCFJd8kaJuNc5aDAAuG1XopkU7h93HvfbNvQaWdQLtwFmUEbDN3GCZ2Mxw6tq5ZSh8d1Chyw",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2/2/1000000000",
|
||||
master: testVec1MasterPrivKey,
|
||||
path: []uint32{0, 1, 2, 2, 1000000000},
|
||||
wantPriv: "dprv3tKkzgLFKaX2VcQTir9JeNHWxikiKokSJtdyj7sYoiDkU3np2rc3DGYPRVmDhb2FFaAk98fnqRotQYTVCRaoyAZiHaoyNoPCFeYA9pEshBT",
|
||||
},
|
||||
|
||||
// Test vector 2
|
||||
{
|
||||
name: "test vector 2 chain m",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{},
|
||||
wantPriv: "dprv3hCznBesA6jBtPKJbQTxRZAKG2gyj8tZKEPaCsV4e9YYFBAgRP2eTSPAeu4r8dTMt9q51j2Vdt5zNqj7jbtovvocrP1qLj6WUTLF9xYQt4y",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{0},
|
||||
wantPriv: "dprv3jMy45BuuDETfxi59P8NTSjHPrNVq4wPRfLgRd57923L2hosj5NUEqiLYQ4i7fJtUpiXZLr2wUeToJY2Tm5sCpAJdajEHDmieVJiPQNXwu9",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{0, 2147483647},
|
||||
wantPriv: "dprv3mgHPRgAnNboAb9edL4RPscKYrNLG77BhPvFe3eiTGPSiigDeXct3WeiZ2QqRrm9TiseBuYWGEG79xkBzazpBGfym1vRXjcEo5KUi4rbhZ1",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{0, 2147483647, 1},
|
||||
wantPriv: "dprv3onqUUAjN1xQUAW1BzwiBa5wisDCE8hX8YcAEgVe1DPvbgHVLauDt2NsZPQX6tJs6ozcQSU9GdsffhueTbxTxFMPEMPxM2iHiooMz2oQHWS",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1/2147483646",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{0, 2147483647, 1, 2147483646},
|
||||
wantPriv: "dprv3r8NTJgGzAjY5cU7sLo5rhT8o2wdco2iqjks6nJoiDACTucrPMcsciccv9skwGMX69uRa8EaZofskV7YyzBDbVi6v4RXbJ4DyeZ6JpUgdUi",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1/2147483646/2",
|
||||
master: testVec2MasterPrivKey,
|
||||
path: []uint32{0, 2147483647, 1, 2147483646, 2},
|
||||
wantPriv: "dprv3sp4xvFP9mL9UUEddSZUxrtNnhe5UcHs5wrpxdZVEFCXoT4EpYeHZJjCDhvVEQFK2KfSHXFmew6MeBuvtrJfQv1BnkiSV7xxUji66uvWasp",
|
||||
},
|
||||
|
||||
// Custom tests to trigger specific conditions.
|
||||
{
|
||||
// Seed 000000000000000000000000000000da.
|
||||
name: "Derived privkey with zero high byte m/0",
|
||||
master: "dprv3jFfEhxvVxy6NJWopujhfg7syQL71xCRgNoGUpQTtjTpCwzigwtCwssQGbRQsby7PBs1Yp8Wu7isu396qeNof13EZuxbCTJVF1xkoFAQHWj",
|
||||
path: []uint32{0},
|
||||
wantPriv: "dprv3mWLns1v1fdLfeu5DKTA6NWQHLF6pFsPSwKCS6q4h4nkjm2DfuH5X2iDnW15jhHTGa3rzxSpvskuXugcbBcUUVWCETKKzjW7ja4V2jL4aw4",
|
||||
},
|
||||
}
|
||||
|
||||
tests:
|
||||
for i, test := range tests {
|
||||
extKey, err := hdkeychain.NewKeyFromString(test.master)
|
||||
if err != nil {
|
||||
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
|
||||
"creating extended key: %v", i, test.name,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, childNum := range test.path {
|
||||
var err error
|
||||
extKey, err = extKey.Child(childNum)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
continue tests
|
||||
}
|
||||
}
|
||||
|
||||
privStr, _ := extKey.String()
|
||||
if privStr != test.wantPriv {
|
||||
t.Errorf("Child #%d (%s): mismatched serialized "+
|
||||
"private extended key -- got: %s, want: %s", i,
|
||||
test.name, privStr, test.wantPriv)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestPublicDerivation tests several vectors which derive public keys from
|
||||
// other public keys works as intended.
|
||||
func TestPublicDerivation(t *testing.T) {
|
||||
// The public extended keys for test vectors in [BIP32].
|
||||
testVec1MasterPubKey := "dpubZF8BRmciAzYoTjXZ3bbRWLVCwUKtTquact3Tr6ye77Rgmw76VyqMb9TB9KpfrvUYEM5d1Au4fQzE2BbtxRjwzGsqnWHmtQP9UV1kxZaqvb6"
|
||||
testVec2MasterPubKey := "dpubZF4LSCdF9YKZfNzTVYhz4RBxsjYXqms8AQnMBHXZ8GUKoRSigG7kQnKiJt5pzk93Q8FxcdVBEkQZruSXduGtWnkwXzGnjbSovQ97dCxqaXc"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
master string
|
||||
path []uint32
|
||||
wantPub string
|
||||
}{
|
||||
// Test vector 1
|
||||
{
|
||||
name: "test vector 1 chain m",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{},
|
||||
wantPub: "dpubZF8BRmciAzYoTjXZ3bbRWLVCwUKtTquact3Tr6ye77Rgmw76VyqMb9TB9KpfrvUYEM5d1Au4fQzE2BbtxRjwzGsqnWHmtQP9UV1kxZaqvb6",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{0},
|
||||
wantPub: "dpubZHm6cmVU9pvfDCe3BY7iESzsEnV6xfi4DfoYvycnWLM9cryzKA84DqJ2CphYq6cfiEXgo9C3YLJA4ou81mavw9NDtNc3bLCWVqJz8Fx8qxB",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{0, 1},
|
||||
wantPub: "dpubZKtA6UTDuxeXV2PcYqoe68u7cgDhbTNbA4dUJoaAvfWzuCcRQCyG5S6dbpDZb2p3B5Y2XxLtD94Nemc8QRV4RspmvGwHvE2FZsfE5Pqpeor",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{0, 1, 2},
|
||||
wantPub: "dpubZMwLXm5dRVEJRvJHU8gNV7RwHeXMRRUnYFD4f6C8uNFfqksD1FCDARTwNPsQB3Pg4LuoKXkZbPnE6woUyedwNYVPvZToT5x4Kt6rs4GKa9c",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2/2",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{0, 1, 2, 2},
|
||||
wantPub: "dpubZPfASfojwk6MhtAtkM6wPdQBr1ycVjoyqs3N51zR1keK6FcBhjBTtdW3Wn3kDLBZqgLnGozu8Gh3FV8GrFGpu3knmGVoF1Z6yGdqLU1Rz1S",
|
||||
},
|
||||
{
|
||||
name: "test vector 1 chain m/0/1/2/2/1000000000",
|
||||
master: testVec1MasterPubKey,
|
||||
path: []uint32{0, 1, 2, 2, 1000000000},
|
||||
wantPub: "dpubZR5Pf8cbUGikESevygwydenBaTsgcvoYnRSi7tygu23PxmVEG4GeMQj54oHFoPyRdt7Pg4sMad56yprQszbNyZVewaNEhDkn112C3mqB1fd",
|
||||
},
|
||||
|
||||
// Test vector 2
|
||||
{
|
||||
name: "test vector 2 chain m",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{},
|
||||
wantPub: "dpubZF4LSCdF9YKZfNzTVYhz4RBxsjYXqms8AQnMBHXZ8GUKoRSigG7kQnKiJt5pzk93Q8FxcdVBEkQZruSXduGtWnkwXzGnjbSovQ97dCxqaXc",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{0},
|
||||
wantPub: "dpubZHJs2Z3PtHbbpaXQCi5wBKPhU8tC5ztBKUYBCYNGKk8eZ1EmBs3MhnLJbxHFMAahGnDnZT7qZxC7AXKP8PB6BDNUZgkG77moNMRmXyQ6s6s",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{0, 2147483647},
|
||||
wantPub: "dpubZJgFEUcAZawGaLZdFEX6FfQBQVgU4bUC5qvDERUTD5dfcB2AQPnJ1dKp1R2DrAzC36BznZG43317s2oBJv3PuaZmA6HqmwMu6vNna4Gfumf",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{0, 2147483647, 1},
|
||||
wantPub: "dpubZLbgtFNyjt3k2cJtg4a3dD2iXPKFTLgNKP8rLC1p5UE3AyfRHLTcYrZ6brg8eUmGvKRrXZ7A3XyVfwGxvYtjfz8514dUoJPkmSnBmC6qQK6",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1/2147483646",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{0, 2147483647, 1, 2147483646},
|
||||
wantPub: "dpubZNyWTupEG35S6d4uN93vWXpGxQuxtW9zuThQbnWpWTHwRCzxREqSSc9eDYivRGiZnEkEhPece5ciSoHtW6Khc729f6eAxjPnBgU38U9hgYw",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1/2147483646/2",
|
||||
master: testVec2MasterPubKey,
|
||||
path: []uint32{0, 2147483647, 1, 2147483646, 2},
|
||||
wantPub: "dpubZRuRErXqhdJaZWD1AzXB6d5w2zw7UZ7ALxiS1gHbnQbVEohBzQzsVwGRzq97pmuE7ToA6DGn2QTH4DexxzdnMvkiYUpk8Nh2KEuYUM2RCeU",
|
||||
},
|
||||
}
|
||||
|
||||
tests:
|
||||
for i, test := range tests {
|
||||
extKey, err := hdkeychain.NewKeyFromString(test.master)
|
||||
if err != nil {
|
||||
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
|
||||
"creating extended key: %v", i, test.name,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, childNum := range test.path {
|
||||
var err error
|
||||
extKey, err = extKey.Child(childNum)
|
||||
if err != nil {
|
||||
t.Errorf("err: %v", err)
|
||||
continue tests
|
||||
}
|
||||
}
|
||||
|
||||
pubStr, _ := extKey.String()
|
||||
if pubStr != test.wantPub {
|
||||
t.Errorf("Child #%d (%s): mismatched serialized "+
|
||||
"public extended key -- got: %s, want: %s", i,
|
||||
test.name, pubStr, test.wantPub)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGenenerateSeed ensures the GenerateSeed function works as intended.
|
||||
func TestGenenerateSeed(t *testing.T) {
|
||||
wantErr := errors.New("seed length must be between 128 and 512 bits")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
length uint8
|
||||
err error
|
||||
}{
|
||||
// Test various valid lengths.
|
||||
{name: "16 bytes", length: 16},
|
||||
{name: "17 bytes", length: 17},
|
||||
{name: "20 bytes", length: 20},
|
||||
{name: "32 bytes", length: 32},
|
||||
{name: "64 bytes", length: 64},
|
||||
|
||||
// Test invalid lengths.
|
||||
{name: "15 bytes", length: 15, err: wantErr},
|
||||
{name: "65 bytes", length: 65, err: wantErr},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
seed, err := hdkeychain.GenerateSeed(test.length)
|
||||
if !reflect.DeepEqual(err, test.err) {
|
||||
t.Errorf("GenerateSeed #%d (%s): unexpected error -- "+
|
||||
"want %v, got %v", i, test.name, test.err, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if test.err == nil && len(seed) != int(test.length) {
|
||||
t.Errorf("GenerateSeed #%d (%s): length mismatch -- "+
|
||||
"got %d, want %d", i, test.name, len(seed),
|
||||
test.length)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestExtendedKeyAPI ensures the API on the ExtendedKey type works as intended.
|
||||
func TestExtendedKeyAPI(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
extKey string
|
||||
isPrivate bool
|
||||
parentFP uint32
|
||||
privKey string
|
||||
privKeyErr error
|
||||
pubKey string
|
||||
address string
|
||||
}{
|
||||
{
|
||||
name: "test vector 1 master node private",
|
||||
extKey: "dprv3hCznBesA6jBteCheVrorP4Nix6oEWFwtk79FaLy1rPzedNGg9Jhy9y596C9uCPMeeKJMvtEGZRaxPKxGuCFgR4uo2EySsA29GsnXcrsby8",
|
||||
isPrivate: true,
|
||||
parentFP: 0,
|
||||
privKey: "33a63922ea4e6686c9fc31daf136888297537f66c1aabe3363df06af0b8274c7",
|
||||
pubKey: "039f2e1d7b50b8451911c64cf745f9ba16193b319212a64096e5679555449d8f37",
|
||||
address: "Dsk8SfRLF2hssYuLcb6Gu4zh19rg2QBEDGs",
|
||||
},
|
||||
{
|
||||
name: "test vector 2 chain m/0/2147483647/1/2147483646/2",
|
||||
extKey: "dpubZRuRErXqhdJaZWD1AzXB6d5w2zw7UZ7ALxiS1gHbnQbVEohBzQzsVwGRzq97pmuE7ToA6DGn2QTH4DexxzdnMvkiYUpk8Nh2KEuYUM2RCeU",
|
||||
isPrivate: false,
|
||||
parentFP: 4220580796,
|
||||
privKeyErr: hdkeychain.ErrNotPrivExtKey,
|
||||
pubKey: "03dceb0b07698ec3d6ac08ae7297e7f5e63d7fda99d3fce1ded31d36badcdd4d36",
|
||||
address: "DsZcjfdSKUrEQxoyjkWEo7dM4YZKhma8wCa",
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
key, err := hdkeychain.NewKeyFromString(test.extKey)
|
||||
if err != nil {
|
||||
t.Errorf("NewKeyFromString #%d (%s): unexpected "+
|
||||
"error: %v", i, test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if key.IsPrivate() != test.isPrivate {
|
||||
t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+
|
||||
"want private %v, got private %v", i, test.name,
|
||||
test.isPrivate, key.IsPrivate())
|
||||
continue
|
||||
}
|
||||
|
||||
parentFP := key.ParentFingerprint()
|
||||
if parentFP != test.parentFP {
|
||||
t.Errorf("ParentFingerprint #%d (%s): mismatched "+
|
||||
"parent fingerprint -- want %d, got %d", i,
|
||||
test.name, test.parentFP, parentFP)
|
||||
continue
|
||||
}
|
||||
|
||||
serializedKey, _ := key.String()
|
||||
if serializedKey != test.extKey {
|
||||
t.Errorf("String #%d (%s): mismatched serialized key "+
|
||||
"-- want %s, got %s", i, test.name, test.extKey,
|
||||
serializedKey)
|
||||
continue
|
||||
}
|
||||
|
||||
privKey, err := key.ECPrivKey()
|
||||
if !reflect.DeepEqual(err, test.privKeyErr) {
|
||||
t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+
|
||||
"%v, got %v", i, test.name, test.privKeyErr, err)
|
||||
continue
|
||||
}
|
||||
if test.privKeyErr == nil {
|
||||
privKeyStr := hex.EncodeToString(privKey.Serialize())
|
||||
if privKeyStr != test.privKey {
|
||||
t.Errorf("ECPrivKey #%d (%s): mismatched "+
|
||||
"private key -- want %s, got %s", i,
|
||||
test.name, test.privKey, privKeyStr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pubKey, err := key.ECPubKey()
|
||||
if err != nil {
|
||||
t.Errorf("ECPubKey #%d (%s): unexpected error: %v", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed())
|
||||
if pubKeyStr != test.pubKey {
|
||||
t.Errorf("ECPubKey #%d (%s): mismatched public key -- "+
|
||||
"want %s, got %s", i, test.name, test.pubKey,
|
||||
pubKeyStr)
|
||||
continue
|
||||
}
|
||||
|
||||
addr, err := key.Address(&chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
t.Errorf("Address #%d (%s): unexpected error: %v", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
if addr.EncodeAddress() != test.address {
|
||||
t.Errorf("Address #%d (%s): mismatched address -- want "+
|
||||
"%s, got %s", i, test.name, test.address,
|
||||
addr.EncodeAddress())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNet ensures the network related APIs work as intended.
|
||||
func TestNet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
origNet *chaincfg.Params
|
||||
newNet *chaincfg.Params
|
||||
newPriv string
|
||||
newPub string
|
||||
isPrivate bool
|
||||
}{
|
||||
// Private extended keys.
|
||||
{
|
||||
name: "mainnet -> simnet",
|
||||
key: "dprv3hCznBesA6jBu46PsJ9vNJoiCj9ouxtfwCBNjUYuXwbbAS4oEkF6Bnp5G3QbBAjRXy4uWWZYmC5Y71s3ovCyPLrCjEkYGPErrueuPPjvNWh",
|
||||
origNet: &chaincfg.MainNetParams,
|
||||
newNet: &chaincfg.SimNetParams,
|
||||
newPriv: "sprvZ9xkGEZkBei2p9e1uBZRQMGtfGEQNGApP1W19PyNRqg9nuEs2X4ynkvAXWaBiGb5WKiaqcbiKgmyB1HYgcX3mnxiUs7UWeWEfe4tnSpbXLv",
|
||||
newPub: "spubVNx6fk6e22GL2diV1D6RmVDdDJ4tmitfkERbwnNyzBD8fha1a4PELZEeNoUfNofdyJS2Y19tFgHZQ62tzKwELiBA3xVeZowLr4DJQ7xGuao",
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
name: "simnet -> mainnet",
|
||||
key: "sprvZ9xkGEZkBei2p9e1uBZRQMGtfGEQNGApP1W19PyNRqg9nuEs2X4ynkvAXWaBiGb5WKiaqcbiKgmyB1HYgcX3mnxiUs7UWeWEfe4tnSpbXLv",
|
||||
origNet: &chaincfg.SimNetParams,
|
||||
newNet: &chaincfg.MainNetParams,
|
||||
newPriv: "dprv3hCznBesA6jBu46PsJ9vNJoiCj9ouxtfwCBNjUYuXwbbAS4oEkF6Bnp5G3QbBAjRXy4uWWZYmC5Y71s3ovCyPLrCjEkYGPErrueuPPjvNWh",
|
||||
newPub: "dpubZ9169KDAEUnyoTzA7pDGtXbxpji5LuUk8johUPVGY2CDsz6S7hahGNL6QmyavE5fgonsepiACAa7FQPsCDeLFnoSSAGiQEQhimBGGK84nye",
|
||||
isPrivate: true,
|
||||
},
|
||||
|
||||
// Public extended keys.
|
||||
{
|
||||
name: "mainnet -> simnet",
|
||||
key: "dpubZ9169KDAEUnyoTzA7pDGtXbxpji5LuUk8johUPVGY2CDsz6S7hahGNL6QmyavE5fgonsepiACAa7FQPsCDeLFnoSSAGiQEQhimBGGK84nye",
|
||||
origNet: &chaincfg.MainNetParams,
|
||||
newNet: &chaincfg.SimNetParams,
|
||||
newPub: "spubVNx6fk6e22GL2diV1D6RmVDdDJ4tmitfkERbwnNyzBD8fha1a4PELZEeNoUfNofdyJS2Y19tFgHZQ62tzKwELiBA3xVeZowLr4DJQ7xGuao",
|
||||
isPrivate: false,
|
||||
},
|
||||
{
|
||||
name: "simnet -> mainnet",
|
||||
key: "spubVNx6fk6e22GL2diV1D6RmVDdDJ4tmitfkERbwnNyzBD8fha1a4PELZEeNoUfNofdyJS2Y19tFgHZQ62tzKwELiBA3xVeZowLr4DJQ7xGuao",
|
||||
origNet: &chaincfg.SimNetParams,
|
||||
newNet: &chaincfg.MainNetParams,
|
||||
newPub: "dpubZ9169KDAEUnyoTzA7pDGtXbxpji5LuUk8johUPVGY2CDsz6S7hahGNL6QmyavE5fgonsepiACAa7FQPsCDeLFnoSSAGiQEQhimBGGK84nye",
|
||||
isPrivate: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
extKey, err := hdkeychain.NewKeyFromString(test.key)
|
||||
if err != nil {
|
||||
t.Errorf("NewKeyFromString #%d (%s): unexpected error "+
|
||||
"creating extended key: %v", i, test.name,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !extKey.IsForNet(test.origNet) {
|
||||
t.Errorf("IsForNet #%d (%s): key is not for expected "+
|
||||
"network %v", i, test.name, test.origNet.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
extKey.SetNet(test.newNet)
|
||||
if !extKey.IsForNet(test.newNet) {
|
||||
t.Errorf("SetNet/IsForNet #%d (%s): key is not for "+
|
||||
"expected network %v", i, test.name,
|
||||
test.newNet.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
if test.isPrivate {
|
||||
privStr, _ := extKey.String()
|
||||
if privStr != test.newPriv {
|
||||
t.Errorf("Serialize #%d (%s): mismatched serialized "+
|
||||
"private extended key -- got: %s, want: %s", i,
|
||||
test.name, privStr, test.newPriv)
|
||||
continue
|
||||
}
|
||||
|
||||
extKey, err = extKey.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter #%d (%s): unexpected error: %v ", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pubStr, _ := extKey.String()
|
||||
if pubStr != test.newPub {
|
||||
t.Errorf("Neuter #%d (%s): mismatched serialized "+
|
||||
"public extended key -- got: %s, want: %s", i,
|
||||
test.name, pubStr, test.newPub)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestErrors performs some negative tests for various invalid cases to ensure
|
||||
// the errors are handled properly.
|
||||
func TestErrors(t *testing.T) {
|
||||
// Should get an error when seed has too few bytes.
|
||||
net := &chaincfg.MainNetParams
|
||||
_, err := hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 15), net)
|
||||
if err != hdkeychain.ErrInvalidSeedLen {
|
||||
t.Errorf("NewMaster: mismatched error -- got: %v, want: %v",
|
||||
err, hdkeychain.ErrInvalidSeedLen)
|
||||
}
|
||||
|
||||
// Should get an error when seed has too many bytes.
|
||||
_, err = hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 65), net)
|
||||
if err != hdkeychain.ErrInvalidSeedLen {
|
||||
t.Errorf("NewMaster: mismatched error -- got: %v, want: %v",
|
||||
err, hdkeychain.ErrInvalidSeedLen)
|
||||
}
|
||||
|
||||
// Generate a new key and neuter it to a public extended key.
|
||||
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
|
||||
if err != nil {
|
||||
t.Errorf("GenerateSeed: unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
extKey, err := hdkeychain.NewMaster(seed, net)
|
||||
if err != nil {
|
||||
t.Errorf("NewMaster: unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
pubKey, err := extKey.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter: unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Deriving a hardened child extended key should fail from a public key.
|
||||
_, err = pubKey.Child(hdkeychain.HardenedKeyStart)
|
||||
if err != hdkeychain.ErrDeriveHardFromPublic {
|
||||
t.Errorf("Child: mismatched error -- got: %v, want: %v",
|
||||
err, hdkeychain.ErrDeriveHardFromPublic)
|
||||
}
|
||||
|
||||
// NewKeyFromString failure tests.
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
err error
|
||||
neuter bool
|
||||
neuterErr error
|
||||
}{
|
||||
{
|
||||
name: "invalid key length",
|
||||
key: "dpub1234",
|
||||
err: hdkeychain.ErrInvalidKeyLen,
|
||||
},
|
||||
{
|
||||
name: "bad checksum",
|
||||
key: "dpubZF6AWaFizAuUcbkZSs8cP8Gxzr6Sg5tLYYM7gEjZMC5GDaSHB4rW4F51zkWyo9U19BnXhc99kkEiPg248bYin8m9b8mGss9nxV6N2QpU8vj",
|
||||
err: hdkeychain.ErrBadChecksum,
|
||||
},
|
||||
{
|
||||
name: "pubkey not on curve",
|
||||
key: "dpubZ9169KDAEUnyoTzA7pDGtXbxpji5LuUk8johUPVGY2CDsz6S7hahGNL6QkeYrUeAPnaJD1MBmrsUnErXScGZdjL6b2gjCRX1Z1GNhLdVCjv",
|
||||
err: errors.New("pubkey [0,50963827496501355358210603252497135226159332537351223778668747140855667399507] isn't on secp256k1 curve"),
|
||||
},
|
||||
{
|
||||
name: "unsupported version",
|
||||
key: "4s9bfpYH9CkJboPNLFC4BhTENPrjfmKwUxesnqxHBjv585bCLzVdQKuKQ5TouA57FkdDskrR695Z5U2wWwDUUVWXPg7V57sLpc9dMgx74LsVZGEB",
|
||||
err: nil,
|
||||
neuter: true,
|
||||
neuterErr: chaincfg.ErrUnknownHDKeyID,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
extKey, err := hdkeychain.NewKeyFromString(test.key)
|
||||
if !reflect.DeepEqual(err, test.err) {
|
||||
t.Errorf("NewKeyFromString #%d (%s): mismatched error "+
|
||||
"-- got: %v, want: %v", i, test.name, err,
|
||||
test.err)
|
||||
continue
|
||||
}
|
||||
|
||||
if test.neuter {
|
||||
_, err := extKey.Neuter()
|
||||
if !reflect.DeepEqual(err, test.neuterErr) {
|
||||
t.Errorf("Neuter #%d (%s): mismatched error "+
|
||||
"-- got: %v, want: %v", i, test.name,
|
||||
err, test.neuterErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestZero ensures that zeroing an extended key works as intended.
|
||||
func TestZero(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
master string
|
||||
extKey string
|
||||
net *chaincfg.Params
|
||||
}{
|
||||
// Test vector 1
|
||||
{
|
||||
name: "test vector 1 chain m",
|
||||
master: "000102030405060708090a0b0c0d0e0f",
|
||||
extKey: "dprv3hCznBesA6jBtmoyVFPfyMSZ1qYZ3WdjdebquvkEfmRfxC9VFEFi2YDaJqHnx7uGe75eGSa3Mn3oHK11hBW7KZUrPxwbCPBmuCi1nwm182s",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
|
||||
// Test vector 2
|
||||
{
|
||||
name: "test vector 2 chain m",
|
||||
master: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
|
||||
extKey: "dprv3hCznBesA6jBtPKJbQTxRZAKG2gyj8tZKEPaCsV4e9YYFBAgRP2eTSPAeu4r8dTMt9q51j2Vdt5zNqj7jbtovvocrP1qLj6WUTLF9xYQt4y",
|
||||
net: &chaincfg.MainNetParams,
|
||||
},
|
||||
}
|
||||
|
||||
// Use a closure to test that a key is zeroed since the tests create
|
||||
// keys in different ways and need to test the same things multiple
|
||||
// times.
|
||||
testZeroed := func(i int, testName string, key *hdkeychain.ExtendedKey) bool {
|
||||
// Zeroing a key should result in it no longer being private
|
||||
if key.IsPrivate() != false {
|
||||
t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+
|
||||
"want private %v, got private %v", i, testName,
|
||||
false, key.IsPrivate())
|
||||
return false
|
||||
}
|
||||
|
||||
parentFP := key.ParentFingerprint()
|
||||
if parentFP != 0 {
|
||||
t.Errorf("ParentFingerprint #%d (%s): mismatched "+
|
||||
"parent fingerprint -- want %d, got %d", i,
|
||||
testName, 0, parentFP)
|
||||
return false
|
||||
}
|
||||
|
||||
wantKey := "zeroed extended key"
|
||||
_, errZeroed := key.String()
|
||||
if errZeroed.Error() != wantKey {
|
||||
t.Errorf("String #%d (%s): mismatched serialized key "+
|
||||
"-- want %s, got %s", i, testName, wantKey,
|
||||
errZeroed)
|
||||
return false
|
||||
}
|
||||
|
||||
wantErr := hdkeychain.ErrNotPrivExtKey
|
||||
_, err := key.ECPrivKey()
|
||||
if !reflect.DeepEqual(err, wantErr) {
|
||||
t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+
|
||||
"%v, got %v", i, testName, wantErr, err)
|
||||
return false
|
||||
}
|
||||
|
||||
wantErr = errors.New("pubkey string is empty")
|
||||
_, err = key.ECPubKey()
|
||||
if !reflect.DeepEqual(err, wantErr) {
|
||||
t.Errorf("ECPubKey #%d (%s): mismatched error: want "+
|
||||
"%v, got %v", i, testName, wantErr, err)
|
||||
return false
|
||||
}
|
||||
|
||||
wantAddr := "DsWuefL3Rgj6NXoMFqqBzxY2nmh87RZyPkv"
|
||||
addr, err := key.Address(&chaincfg.MainNetParams)
|
||||
if err != nil {
|
||||
t.Errorf("Addres s #%d (%s): unexpected error: %v", i,
|
||||
testName, err)
|
||||
return false
|
||||
}
|
||||
if addr.EncodeAddress() != wantAddr {
|
||||
t.Errorf("Address #%d (%s): mismatched address -- want "+
|
||||
"%s, got %s", i, testName, wantAddr,
|
||||
addr.EncodeAddress())
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// Create new key from seed and get the neutered version.
|
||||
masterSeed, err := hex.DecodeString(test.master)
|
||||
if err != nil {
|
||||
t.Errorf("DecodeString #%d (%s): unexpected error: %v",
|
||||
i, test.name, err)
|
||||
continue
|
||||
}
|
||||
key, err := hdkeychain.NewMaster(masterSeed, test.net)
|
||||
if err != nil {
|
||||
t.Errorf("NewMaster #%d (%s): unexpected error when "+
|
||||
"creating new master key: %v", i, test.name,
|
||||
err)
|
||||
continue
|
||||
}
|
||||
neuteredKey, err := key.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure both non-neutered and neutered keys are zeroed
|
||||
// properly.
|
||||
key.Zero()
|
||||
if !testZeroed(i, test.name+" from seed not neutered", key) {
|
||||
continue
|
||||
}
|
||||
neuteredKey.Zero()
|
||||
if !testZeroed(i, test.name+" from seed neutered", key) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Deserialize key and get the neutered version.
|
||||
key, err = hdkeychain.NewKeyFromString(test.extKey)
|
||||
if err != nil {
|
||||
t.Errorf("NewKeyFromString #%d (%s): unexpected "+
|
||||
"error: %v", i, test.name, err)
|
||||
continue
|
||||
}
|
||||
neuteredKey, err = key.Neuter()
|
||||
if err != nil {
|
||||
t.Errorf("Neuter #%d (%s): unexpected error: %v", i,
|
||||
test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure both non-neutered and neutered keys are zeroed
|
||||
// properly.
|
||||
key.Zero()
|
||||
if !testZeroed(i, test.name+" deserialized not neutered", key) {
|
||||
continue
|
||||
}
|
||||
neuteredKey.Zero()
|
||||
if !testZeroed(i, test.name+" deserialized neutered", key) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
74
dcrutil/internal_test.go
Normal file
74
dcrutil/internal_test.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
This test file is part of the btcutil package rather than than the
|
||||
btcutil_test package so it can bridge access to the internals to properly test
|
||||
cases which are either not possible or can't reliably be tested via the public
|
||||
interface. The functions are only exported while the tests are being run.
|
||||
*/
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"github.com/decred/base58"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
// SetBlockBytes sets the internal serialized block byte buffer to the passed
|
||||
// buffer. It is used to inject errors and is only available to the test
|
||||
// package.
|
||||
func (b *Block) SetBlockBytes(buf []byte) {
|
||||
b.serializedBlock = buf
|
||||
}
|
||||
|
||||
// TstAppDataDir makes the internal appDataDir function available to the test
|
||||
// package.
|
||||
func TstAppDataDir(goos, appName string, roaming bool) string {
|
||||
return appDataDir(goos, appName, roaming)
|
||||
}
|
||||
|
||||
// TstAddressPubKeyHash makes an AddressPubKeyHash, setting the
|
||||
// unexported fields with the parameters hash and netID.
|
||||
func TstAddressPubKeyHash(hash [ripemd160.Size]byte,
|
||||
netID [2]byte) *AddressPubKeyHash {
|
||||
return &AddressPubKeyHash{
|
||||
hash: hash,
|
||||
netID: netID,
|
||||
}
|
||||
}
|
||||
|
||||
// TstAddressScriptHash makes an AddressScriptHash, setting the
|
||||
// unexported fields with the parameters hash and netID.
|
||||
func TstAddressScriptHash(hash [ripemd160.Size]byte,
|
||||
netID [2]byte) *AddressScriptHash {
|
||||
|
||||
return &AddressScriptHash{
|
||||
hash: hash,
|
||||
netID: netID,
|
||||
}
|
||||
}
|
||||
|
||||
// TstAddressPubKey makes an AddressPubKey, setting the unexported fields with
|
||||
// the parameters.
|
||||
func TstAddressPubKey(serializedPubKey []byte, pubKeyFormat PubKeyFormat,
|
||||
netID [2]byte) *AddressSecpPubKey {
|
||||
|
||||
pubKey, _ := chainec.Secp256k1.ParsePubKey(serializedPubKey)
|
||||
return &AddressSecpPubKey{
|
||||
pubKeyFormat: pubKeyFormat,
|
||||
pubKey: chainec.PublicKey(pubKey),
|
||||
pubKeyHashID: netID,
|
||||
}
|
||||
}
|
||||
|
||||
// TstAddressSAddr returns the expected script address bytes for
|
||||
// P2PKH and P2SH decred addresses.
|
||||
func TstAddressSAddr(addr string) []byte {
|
||||
decoded := base58.Decode(addr)
|
||||
return decoded[2 : 2+ripemd160.Size]
|
||||
}
|
||||
224
dcrutil/tx.go
Normal file
224
dcrutil/tx.go
Normal file
@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2017 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// assertTransactionImmutability throws a panic when a transaction has been
|
||||
// mutated.
|
||||
var assertTransactionImmutability = false
|
||||
|
||||
// TxIndexUnknown is the value returned for a transaction index that is unknown.
|
||||
// This is typically because the transaction has not been inserted into a block
|
||||
// yet.
|
||||
const TxIndexUnknown = -1
|
||||
|
||||
// Tx defines a transaction that provides easier and more efficient manipulation
|
||||
// of raw transactions. It also memoizes the hash for the transaction on its
|
||||
// first access so subsequent accesses don't have to repeat the relatively
|
||||
// expensive hashing operations.
|
||||
type Tx struct {
|
||||
hash chainhash.Hash // Cached transaction hash
|
||||
msgTx *wire.MsgTx // Underlying MsgTx
|
||||
txTree int8 // Indicates which tx tree the tx is found in
|
||||
txIndex int // Position within a block or TxIndexUnknown
|
||||
}
|
||||
|
||||
// MsgTx returns the underlying wire.MsgTx for the transaction.
|
||||
func (t *Tx) MsgTx() *wire.MsgTx {
|
||||
// Return the cached transaction.
|
||||
return t.msgTx
|
||||
}
|
||||
|
||||
// Hash returns the hash of the transaction. This is equivalent to
|
||||
// calling TxHash on the underlying wire.MsgTx, however it caches the
|
||||
// result so subsequent calls are more efficient.
|
||||
func (t *Tx) Hash() *chainhash.Hash {
|
||||
if assertTransactionImmutability {
|
||||
hash := t.msgTx.TxHash()
|
||||
if !hash.IsEqual(&t.hash) {
|
||||
str := fmt.Sprintf("ASSERT: mutated util.tx detected, old hash %v, "+
|
||||
"new hash %v",
|
||||
t.hash,
|
||||
hash)
|
||||
panic(str)
|
||||
}
|
||||
}
|
||||
return &t.hash
|
||||
}
|
||||
|
||||
// Index returns the saved index of the transaction within a block. This value
|
||||
// will be TxIndexUnknown if it hasn't already explicitly been set.
|
||||
func (t *Tx) Index() int {
|
||||
return t.txIndex
|
||||
}
|
||||
|
||||
// SetIndex sets the index of the transaction in within a block.
|
||||
func (t *Tx) SetIndex(index int) {
|
||||
t.txIndex = index
|
||||
}
|
||||
|
||||
// Tree returns the saved tree of the transaction within a block. This value
|
||||
// will be TxTreeUnknown if it hasn't already explicitly been set.
|
||||
func (t *Tx) Tree() int8 {
|
||||
return t.txTree
|
||||
}
|
||||
|
||||
// SetTree sets the tree of the transaction in within a block.
|
||||
func (t *Tx) SetTree(tree int8) {
|
||||
t.txTree = tree
|
||||
}
|
||||
|
||||
// NewTx returns a new instance of a transaction given an underlying
|
||||
// wire.MsgTx. See Tx.
|
||||
func NewTx(msgTx *wire.MsgTx) *Tx {
|
||||
return &Tx{
|
||||
hash: msgTx.TxHash(),
|
||||
msgTx: msgTx,
|
||||
txTree: wire.TxTreeUnknown,
|
||||
txIndex: TxIndexUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTxDeep returns a new instance of a transaction given an underlying
|
||||
// wire.MsgTx. Until NewTx, it completely copies the data in the msgTx
|
||||
// so that there are new memory allocations, in case you were to somewhere
|
||||
// else modify the data assigned to these pointers.
|
||||
func NewTxDeep(msgTx *wire.MsgTx) *Tx {
|
||||
txIns := make([]*wire.TxIn, len(msgTx.TxIn), len(msgTx.TxIn))
|
||||
txOuts := make([]*wire.TxOut, len(msgTx.TxOut), len(msgTx.TxOut))
|
||||
|
||||
for i, txin := range msgTx.TxIn {
|
||||
sigScript := make([]byte, len(txin.SignatureScript),
|
||||
len(txin.SignatureScript))
|
||||
copy(sigScript[:], txin.SignatureScript[:])
|
||||
|
||||
txIns[i] = &wire.TxIn{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: txin.PreviousOutPoint.Hash,
|
||||
Index: txin.PreviousOutPoint.Index,
|
||||
Tree: txin.PreviousOutPoint.Tree,
|
||||
},
|
||||
Sequence: txin.Sequence,
|
||||
ValueIn: txin.ValueIn,
|
||||
BlockHeight: txin.BlockHeight,
|
||||
BlockIndex: txin.BlockIndex,
|
||||
SignatureScript: sigScript,
|
||||
}
|
||||
}
|
||||
|
||||
for i, txout := range msgTx.TxOut {
|
||||
pkScript := make([]byte, len(txout.PkScript),
|
||||
len(txout.PkScript))
|
||||
copy(pkScript[:], txout.PkScript[:])
|
||||
|
||||
txOuts[i] = &wire.TxOut{
|
||||
Value: txout.Value,
|
||||
Version: txout.Version,
|
||||
PkScript: pkScript,
|
||||
}
|
||||
}
|
||||
|
||||
mtx := &wire.MsgTx{
|
||||
CachedHash: nil,
|
||||
SerType: msgTx.SerType,
|
||||
Version: msgTx.Version,
|
||||
TxIn: txIns,
|
||||
TxOut: txOuts,
|
||||
LockTime: msgTx.LockTime,
|
||||
Expiry: msgTx.Expiry,
|
||||
}
|
||||
|
||||
return &Tx{
|
||||
hash: mtx.TxHash(),
|
||||
msgTx: mtx,
|
||||
txTree: wire.TxTreeUnknown,
|
||||
txIndex: TxIndexUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTxDeepTxIns is used to deep copy a transaction, maintaining the old
|
||||
// pointers to the TxOuts while replacing the old pointers to the TxIns with
|
||||
// deep copies. This is to prevent races when the fraud proofs for the
|
||||
// transactions are set by the miner.
|
||||
func NewTxDeepTxIns(msgTx *wire.MsgTx) *Tx {
|
||||
if msgTx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
newMsgTx := new(wire.MsgTx)
|
||||
|
||||
// Copy the fixed fields.
|
||||
newMsgTx.Version = msgTx.Version
|
||||
newMsgTx.LockTime = msgTx.LockTime
|
||||
newMsgTx.Expiry = msgTx.Expiry
|
||||
|
||||
// Copy the TxIns deeply.
|
||||
for _, txIn := range msgTx.TxIn {
|
||||
sigScrLen := len(txIn.SignatureScript)
|
||||
sigScrCopy := make([]byte, sigScrLen, sigScrLen)
|
||||
|
||||
txInCopy := new(wire.TxIn)
|
||||
txInCopy.PreviousOutPoint.Hash = txIn.PreviousOutPoint.Hash
|
||||
txInCopy.PreviousOutPoint.Index = txIn.PreviousOutPoint.Index
|
||||
txInCopy.PreviousOutPoint.Tree = txIn.PreviousOutPoint.Tree
|
||||
|
||||
txInCopy.Sequence = txIn.Sequence
|
||||
txInCopy.ValueIn = txIn.ValueIn
|
||||
txInCopy.BlockHeight = txIn.BlockHeight
|
||||
txInCopy.BlockIndex = txIn.BlockIndex
|
||||
|
||||
txInCopy.SignatureScript = sigScrCopy
|
||||
|
||||
newMsgTx.AddTxIn(txIn)
|
||||
}
|
||||
|
||||
// Shallow copy the TxOuts.
|
||||
for _, txOut := range msgTx.TxOut {
|
||||
newMsgTx.AddTxOut(txOut)
|
||||
}
|
||||
|
||||
return &Tx{
|
||||
hash: msgTx.TxHash(),
|
||||
msgTx: msgTx,
|
||||
txTree: wire.TxTreeUnknown,
|
||||
txIndex: TxIndexUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTxFromBytes returns a new instance of a transaction given the
|
||||
// serialized bytes. See Tx.
|
||||
func NewTxFromBytes(serializedTx []byte) (*Tx, error) {
|
||||
br := bytes.NewReader(serializedTx)
|
||||
return NewTxFromReader(br)
|
||||
}
|
||||
|
||||
// NewTxFromReader returns a new instance of a transaction given a
|
||||
// Reader to deserialize the transaction. See Tx.
|
||||
func NewTxFromReader(r io.Reader) (*Tx, error) {
|
||||
// Deserialize the bytes into a MsgTx.
|
||||
var msgTx wire.MsgTx
|
||||
err := msgTx.Deserialize(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := Tx{
|
||||
hash: msgTx.TxHash(),
|
||||
msgTx: &msgTx,
|
||||
txTree: wire.TxTreeUnknown,
|
||||
txIndex: TxIndexUnknown,
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
124
dcrutil/tx_test.go
Normal file
124
dcrutil/tx_test.go
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// TestTx tests the API for Tx.
|
||||
func TestTx(t *testing.T) {
|
||||
testTx := Block100000.Transactions[0]
|
||||
tx := dcrutil.NewTx(testTx)
|
||||
|
||||
// Ensure we get the same data back out.
|
||||
if msgTx := tx.MsgTx(); !reflect.DeepEqual(msgTx, testTx) {
|
||||
t.Errorf("MsgTx: mismatched MsgTx - got %v, want %v",
|
||||
spew.Sdump(msgTx), spew.Sdump(testTx))
|
||||
}
|
||||
|
||||
// Ensure transaction index set and get work properly.
|
||||
wantIndex := 0
|
||||
tx.SetIndex(0)
|
||||
if gotIndex := tx.Index(); gotIndex != wantIndex {
|
||||
t.Errorf("Index: mismatched index - got %v, want %v",
|
||||
gotIndex, wantIndex)
|
||||
}
|
||||
|
||||
// Ensure tree type set and get work properly.
|
||||
wantTree := int8(0)
|
||||
tx.SetTree(0)
|
||||
if gotTree := tx.Tree(); gotTree != wantTree {
|
||||
t.Errorf("Index: mismatched index - got %v, want %v",
|
||||
gotTree, wantTree)
|
||||
}
|
||||
|
||||
// Ensure stake transaction index set and get work properly.
|
||||
wantIndex = 0
|
||||
tx.SetIndex(0)
|
||||
if gotIndex := tx.Index(); gotIndex != wantIndex {
|
||||
t.Errorf("Index: mismatched index - got %v, want %v",
|
||||
gotIndex, wantIndex)
|
||||
}
|
||||
|
||||
// Ensure tree type set and get work properly.
|
||||
wantTree = int8(1)
|
||||
tx.SetTree(1)
|
||||
if gotTree := tx.Tree(); gotTree != wantTree {
|
||||
t.Errorf("Index: mismatched index - got %v, want %v",
|
||||
gotTree, wantTree)
|
||||
}
|
||||
|
||||
// Hash for block 100,000 transaction 0.
|
||||
wantHashStr := "1cbd9fe1a143a265cc819ff9d8132a7cbc4ca48eb68c0de39cfdf7ecf42cbbd1"
|
||||
wantHash, err := chainhash.NewHashFromStr(wantHashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Request the hash multiple times to test generation and caching.
|
||||
for i := 0; i < 2; i++ {
|
||||
hash := tx.Hash()
|
||||
if !hash.IsEqual(wantHash) {
|
||||
t.Errorf("Hash #%d mismatched hash - got %v, want %v", i,
|
||||
hash, wantHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewTxFromBytes tests creation of a Tx from serialized bytes.
|
||||
func TestNewTxFromBytes(t *testing.T) {
|
||||
// Serialize the test transaction.
|
||||
testTx := Block100000.Transactions[0]
|
||||
var testTxBuf bytes.Buffer
|
||||
testTxBuf.Grow(testTx.SerializeSize())
|
||||
err := testTx.Serialize(&testTxBuf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
testTxBytes := testTxBuf.Bytes()
|
||||
|
||||
// Create a new transaction from the serialized bytes.
|
||||
tx, err := dcrutil.NewTxFromBytes(testTxBytes)
|
||||
if err != nil {
|
||||
t.Errorf("NewTxFromBytes: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure the generated MsgTx is correct.
|
||||
if msgTx := tx.MsgTx(); !reflect.DeepEqual(msgTx, testTx) {
|
||||
t.Errorf("MsgTx: mismatched MsgTx - got %v, want %v",
|
||||
spew.Sdump(msgTx), spew.Sdump(testTx))
|
||||
}
|
||||
}
|
||||
|
||||
// TestTxErrors tests the error paths for the Tx API.
|
||||
func TestTxErrors(t *testing.T) {
|
||||
// Serialize the test transaction.
|
||||
testTx := Block100000.Transactions[0]
|
||||
var testTxBuf bytes.Buffer
|
||||
testTxBuf.Grow(testTx.SerializeSize())
|
||||
err := testTx.Serialize(&testTxBuf)
|
||||
if err != nil {
|
||||
t.Errorf("Serialize: %v", err)
|
||||
}
|
||||
testTxBytes := testTxBuf.Bytes()
|
||||
|
||||
// Truncate the transaction byte buffer to force errors.
|
||||
shortBytes := testTxBytes[:4]
|
||||
_, err = dcrutil.NewTxFromBytes(shortBytes)
|
||||
if err != io.EOF {
|
||||
t.Errorf("NewTxFromBytes: did not get expected error - "+
|
||||
"got %v, want %v", err, io.EOF)
|
||||
}
|
||||
}
|
||||
165
dcrutil/wif.go
Normal file
165
dcrutil/wif.go
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/decred/base58"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// ErrMalformedPrivateKey describes an error where a WIF-encoded private
|
||||
// key cannot be decoded due to being improperly formatted. This may occur
|
||||
// if the byte length is incorrect or an unexpected magic number was
|
||||
// encountered.
|
||||
var ErrMalformedPrivateKey = errors.New("malformed private key")
|
||||
|
||||
// WIF contains the individual components described by the Wallet Import Format
|
||||
// (WIF). A WIF string is typically used to represent a private key and its
|
||||
// associated address in a way that may be easily copied and imported into or
|
||||
// exported from wallet software. WIF strings may be decoded into this
|
||||
// structure by calling DecodeWIF or created with a user-provided private key
|
||||
// by calling NewWIF.
|
||||
type WIF struct {
|
||||
// ecType is the type of ECDSA used.
|
||||
ecType int
|
||||
|
||||
// PrivKey is the private key being imported or exported.
|
||||
PrivKey chainec.PrivateKey
|
||||
|
||||
// netID is the network identifier byte used when
|
||||
// WIF encoding the private key.
|
||||
netID [2]byte
|
||||
}
|
||||
|
||||
// NewWIF creates a new WIF structure to export an address and its private key
|
||||
// as a string encoded in the Wallet Import Format. The compress argument
|
||||
// specifies whether the address intended to be imported or exported was created
|
||||
// by serializing the public key compressed rather than uncompressed.
|
||||
func NewWIF(privKey chainec.PrivateKey, net *chaincfg.Params, ecType int) (*WIF,
|
||||
error) {
|
||||
if net == nil {
|
||||
return nil, errors.New("no network")
|
||||
}
|
||||
return &WIF{ecType, privKey, net.PrivateKeyID}, nil
|
||||
}
|
||||
|
||||
// IsForNet returns whether or not the decoded WIF structure is associated
|
||||
// with the passed network.
|
||||
func (w *WIF) IsForNet(net *chaincfg.Params) bool {
|
||||
return w.netID == net.PrivateKeyID
|
||||
}
|
||||
|
||||
// DecodeWIF creates a new WIF structure by decoding the string encoding of
|
||||
// the import format.
|
||||
//
|
||||
// The WIF string must be a base58-encoded string of the following byte
|
||||
// sequence:
|
||||
//
|
||||
// * 2 bytes to identify the network, must be 0x80 for mainnet or 0xef for testnet
|
||||
// * 1 byte for ECDSA type
|
||||
// * 32 bytes of a binary-encoded, big-endian, zero-padded private key
|
||||
// * 4 bytes of checksum, must equal the first four bytes of the double SHA256
|
||||
// of every byte before the checksum in this sequence
|
||||
//
|
||||
// If the base58-decoded byte sequence does not match this, DecodeWIF will
|
||||
// return a non-nil error. ErrMalformedPrivateKey is returned when the WIF
|
||||
// is of an impossible length. ErrChecksumMismatch is returned if the
|
||||
// expected WIF checksum does not match the calculated checksum.
|
||||
func DecodeWIF(wif string) (*WIF, error) {
|
||||
decoded := base58.Decode(wif)
|
||||
decodedLen := len(decoded)
|
||||
|
||||
if decodedLen != 39 {
|
||||
return nil, ErrMalformedPrivateKey
|
||||
}
|
||||
|
||||
// Checksum is first four bytes of hash of the identifier byte
|
||||
// and privKey. Verify this matches the final 4 bytes of the decoded
|
||||
// private key.
|
||||
cksum := chainhash.HashB(decoded[:decodedLen-4])
|
||||
if !bytes.Equal(cksum[:4], decoded[decodedLen-4:]) {
|
||||
return nil, ErrChecksumMismatch
|
||||
}
|
||||
|
||||
netID := [2]byte{decoded[0], decoded[1]}
|
||||
var privKey chainec.PrivateKey
|
||||
|
||||
ecType := 0
|
||||
switch int(decoded[2]) {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
privKeyBytes := decoded[3 : 3+chainec.Secp256k1.PrivKeyBytesLen()]
|
||||
privKey, _ = chainec.Secp256k1.PrivKeyFromScalar(privKeyBytes)
|
||||
ecType = chainec.ECTypeSecp256k1
|
||||
case chainec.ECTypeEdwards:
|
||||
privKeyBytes := decoded[3 : 3+32]
|
||||
privKey, _ = chainec.Edwards.PrivKeyFromScalar(privKeyBytes)
|
||||
ecType = chainec.ECTypeEdwards
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
privKeyBytes := decoded[3 : 3+chainec.SecSchnorr.PrivKeyBytesLen()]
|
||||
privKey, _ = chainec.SecSchnorr.PrivKeyFromScalar(privKeyBytes)
|
||||
ecType = chainec.ECTypeSecSchnorr
|
||||
}
|
||||
|
||||
return &WIF{ecType, privKey, netID}, nil
|
||||
}
|
||||
|
||||
// String creates the Wallet Import Format string encoding of a WIF structure.
|
||||
// See DecodeWIF for a detailed breakdown of the format and requirements of
|
||||
// a valid WIF string.
|
||||
func (w *WIF) String() string {
|
||||
// Precalculate size. Maximum number of bytes before base58 encoding
|
||||
// is two bytes for the network, one byte for the ECDSA type, 32 bytes
|
||||
// of private key and finally four bytes of checksum.
|
||||
encodeLen := 2 + 1 + 32 + 4
|
||||
|
||||
a := make([]byte, 0, encodeLen)
|
||||
a = append(a, w.netID[:]...)
|
||||
a = append(a, byte(w.ecType))
|
||||
a = append(a, w.PrivKey.Serialize()...)
|
||||
|
||||
cksum := chainhash.HashB(a)
|
||||
a = append(a, cksum[:4]...)
|
||||
return base58.Encode(a)
|
||||
}
|
||||
|
||||
// SerializePubKey serializes the associated public key of the imported or
|
||||
// exported private key in compressed format. The serialization format
|
||||
// chosen depends on the value of w.ecType.
|
||||
func (w *WIF) SerializePubKey() []byte {
|
||||
pkx, pky := w.PrivKey.Public()
|
||||
var pk chainec.PublicKey
|
||||
|
||||
switch w.ecType {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
pk = chainec.Secp256k1.NewPublicKey(pkx, pky)
|
||||
case chainec.ECTypeEdwards:
|
||||
pk = chainec.Edwards.NewPublicKey(pkx, pky)
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
pk = chainec.SecSchnorr.NewPublicKey(pkx, pky)
|
||||
}
|
||||
|
||||
return pk.SerializeCompressed()
|
||||
}
|
||||
|
||||
// DSA returns the ECDSA type for the private key.
|
||||
func (w *WIF) DSA() int {
|
||||
return w.ecType
|
||||
}
|
||||
|
||||
// paddedAppend appends the src byte slice to dst, returning the new slice.
|
||||
// If the length of the source is smaller than the passed size, leading zero
|
||||
// bytes are appended to the dst slice before appending src.
|
||||
func paddedAppend(size uint, dst, src []byte) []byte {
|
||||
for i := 0; i < int(size)-len(src); i++ {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
return append(dst, src...)
|
||||
}
|
||||
161
dcrutil/wif_test.go
Normal file
161
dcrutil/wif_test.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2013, 2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dcrutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
. "github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
func TestEncodeDecodeWIF(t *testing.T) {
|
||||
suites := []int{
|
||||
chainec.ECTypeSecp256k1,
|
||||
chainec.ECTypeEdwards,
|
||||
chainec.ECTypeSecSchnorr,
|
||||
}
|
||||
for _, suite := range suites {
|
||||
var priv1, priv2 chainec.PrivateKey
|
||||
switch suite {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
priv1, _ = chainec.Secp256k1.PrivKeyFromBytes([]byte{
|
||||
0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27,
|
||||
0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11,
|
||||
0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b,
|
||||
0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d})
|
||||
|
||||
priv2, _ = chainec.Secp256k1.PrivKeyFromBytes([]byte{
|
||||
0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6,
|
||||
0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81,
|
||||
0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9,
|
||||
0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98})
|
||||
case chainec.ECTypeEdwards:
|
||||
priv1, _ = chainec.Edwards.PrivKeyFromScalar([]byte{
|
||||
0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27,
|
||||
0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11,
|
||||
0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b,
|
||||
0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d})
|
||||
|
||||
priv2, _ = chainec.Edwards.PrivKeyFromScalar([]byte{
|
||||
0x0c, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6,
|
||||
0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81,
|
||||
0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9,
|
||||
0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98})
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
priv1, _ = chainec.SecSchnorr.PrivKeyFromBytes([]byte{
|
||||
0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27,
|
||||
0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11,
|
||||
0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b,
|
||||
0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d})
|
||||
|
||||
priv2, _ = chainec.SecSchnorr.PrivKeyFromBytes([]byte{
|
||||
0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6,
|
||||
0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81,
|
||||
0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9,
|
||||
0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98})
|
||||
}
|
||||
|
||||
wif1, err := NewWIF(priv1, &chaincfg.MainNetParams, suite)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wif2, err := NewWIF(priv2, &chaincfg.TestNet2Params, suite)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wif3, err := NewWIF(priv2, &chaincfg.SimNetParams, suite)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var tests []struct {
|
||||
wif *WIF
|
||||
encoded string
|
||||
}
|
||||
|
||||
switch suite {
|
||||
case chainec.ECTypeSecp256k1:
|
||||
tests = []struct {
|
||||
wif *WIF
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
wif1,
|
||||
"PmQdMn8xafwaQouk8ngs1CccRCB1ZmsqQxBaxNR4vhQi5a5QB5716",
|
||||
},
|
||||
{
|
||||
wif2,
|
||||
"PtWVDUidYaiiNT5e2Sfb1Ah4evbaSopZJkkpFBuzkJYcYteugvdFg",
|
||||
},
|
||||
{
|
||||
wif3,
|
||||
"PsURoUb7FMeJQdTYea8pkbUQFBZAsxtfDcfTLGja5sCLZvLZWRtjK",
|
||||
},
|
||||
}
|
||||
case chainec.ECTypeEdwards:
|
||||
tests = []struct {
|
||||
wif *WIF
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
wif1,
|
||||
"PmQfJXKC2ho1633ZiVbSdCZw1y68BVXYFpyE2UfDcbQN5xa3DByDn",
|
||||
},
|
||||
{
|
||||
wif2,
|
||||
"PtWVaBGeCfbFQfgqFew8YvdrSH5TH439K7rvpo3aWnSfDvyK8ijbK",
|
||||
},
|
||||
{
|
||||
wif3,
|
||||
"PsUSAB97uSWqSr4jsnQNJMRC2Y33iD7FDymZuss9rM6PExexSPyTQ",
|
||||
},
|
||||
}
|
||||
case chainec.ECTypeSecSchnorr:
|
||||
tests = []struct {
|
||||
wif *WIF
|
||||
encoded string
|
||||
}{
|
||||
{
|
||||
wif1,
|
||||
"PmQhFGVRUjeRmGBPJCW2FCXFck1EoDBF6hks6auNJVQ26M4h73W9W",
|
||||
},
|
||||
{
|
||||
wif2,
|
||||
"PtWZ6y56SeRZiuMHBrUkFAbhrURogF7xzWL6PQQJ86XvZfeE3jf1a",
|
||||
},
|
||||
{
|
||||
wif3,
|
||||
"PsUVgxwa9RM9m5jBoywyzbP3SjPQ7QC4uNEjUVDsTfBeahKkmETvQ",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// Test that encoding the WIF structure matches the expected string.
|
||||
s := test.wif.String()
|
||||
if s != test.encoded {
|
||||
t.Errorf("TestEncodeDecodePrivateKey failed: want '%s', got '%s'",
|
||||
test.encoded, s)
|
||||
continue
|
||||
}
|
||||
|
||||
// Test that decoding the expected string results in the original WIF
|
||||
// structure.
|
||||
w, err := DecodeWIF(test.encoded)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
if got := w.String(); got != test.encoded {
|
||||
t.Errorf("NewWIF failed: want '%v', got '%v'", test.wif, got)
|
||||
}
|
||||
|
||||
w.SerializePubKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,7 +216,7 @@ information.
|
||||
Decred scripts
|
||||
* [database](https://github.com/decred/dcrd/tree/master/database) -
|
||||
Provides a database interface for the Decred block chain
|
||||
* [dcrutil](https://github.com/decred/dcrutil) - Provides Decred-specific
|
||||
* [dcrutil](https://github.com/decred/dcrd/tree/master/dcrutil) - Provides Decred-specific
|
||||
convenience functions and types
|
||||
* [chainhash](https://github.com/decred/dcrd/tree/master/chaincfg/chainhash) -
|
||||
Provides a generic hash type and associated functions that allows the
|
||||
|
||||
@ -1015,7 +1015,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrrpcclient"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
@ -1076,7 +1076,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrrpcclient"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@ -1167,7 +1167,7 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/decred/dcrrpcclient"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
@ -21,10 +21,10 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/mining"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -18,9 +18,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrec/secp256k1"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// fakeChain is used by the pool harness to provide generated test utxos and
|
||||
|
||||
@ -11,9 +11,9 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/blockchain"
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -15,9 +15,9 @@ import (
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainec"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// TestCalcMinRequiredTxRelayFee tests the calcMinRequiredTxRelayFee API.
|
||||
|
||||
@ -18,11 +18,11 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/mempool"
|
||||
"github.com/decred/dcrd/mining"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// TxDesc is a descriptor about a transaction in a transaction source along with
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
package mining
|
||||
|
||||
import "github.com/decred/dcrutil"
|
||||
import "github.com/decred/dcrd/dcrutil"
|
||||
|
||||
// Policy houses the policy (configuration parameters) which is used to control
|
||||
// the generation of block templates. See the documentation for
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// FutureGetBestBlockHashResult is a future promise to deliver the result of a
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/rpcclient"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/rpcclient"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrutil"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
)
|
||||
|
||||
// FutureGenerateResult is a future promise to deliver the result of a
|
||||
|
||||
@ -15,8 +15,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/dcrjson"
|
||||
"github.com/decred/dcrd/dcrutil"
|
||||
"github.com/decred/dcrd/wire"
|
||||
"github.com/decred/dcrutil"
|
||||
)
|
||||
|
||||
// SigHashType enumerates the available signature hashing types that the
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user