Release v0.1.0
This commit is contained in:
parent
41e6aad190
commit
d301c63596
|
@ -1,12 +1,2 @@
|
|||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
.data
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/muun/libwallet"
|
||||
)
|
||||
|
||||
type signingDetails struct {
|
||||
RedeemScript []byte
|
||||
Address libwallet.MuunAddress
|
||||
}
|
||||
|
||||
type AddressGenerator struct {
|
||||
addrs map[string]signingDetails
|
||||
userKey *libwallet.HDPrivateKey
|
||||
muunKey *libwallet.HDPrivateKey
|
||||
}
|
||||
|
||||
func NewAddressGenerator(userKey, muunKey *libwallet.HDPrivateKey) *AddressGenerator {
|
||||
return &AddressGenerator{
|
||||
addrs: make(map[string]signingDetails),
|
||||
userKey: userKey,
|
||||
muunKey: muunKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) Addresses() map[string]signingDetails {
|
||||
return g.addrs
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) Generate() {
|
||||
g.generateChangeAddrs()
|
||||
g.generateExternalAddrs()
|
||||
g.generateContactAddrs(100)
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) generateChangeAddrs() {
|
||||
const changePath = "m/1'/1'/0"
|
||||
changeUserKey, _ := g.userKey.DeriveTo(changePath)
|
||||
changeMuunKey, _ := g.muunKey.DeriveTo(changePath)
|
||||
|
||||
g.deriveTree(changeUserKey, changeMuunKey, 2000, "change")
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) generateExternalAddrs() {
|
||||
const externalPath = "m/1'/1'/1"
|
||||
externalUserKey, _ := g.userKey.DeriveTo(externalPath)
|
||||
externalMuunKey, _ := g.muunKey.DeriveTo(externalPath)
|
||||
|
||||
g.deriveTree(externalUserKey, externalMuunKey, 2000, "external")
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) generateContactAddrs(numContacts int64) {
|
||||
const addressPath = "m/1'/1'/2"
|
||||
contactUserKey, _ := g.userKey.DeriveTo(addressPath)
|
||||
contactMuunKey, _ := g.muunKey.DeriveTo(addressPath)
|
||||
for i := int64(0); i <= numContacts; i++ {
|
||||
partialContactUserKey, _ := contactUserKey.DerivedAt(i, false)
|
||||
partialMuunUserKey, _ := contactMuunKey.DerivedAt(i, false)
|
||||
|
||||
branch := fmt.Sprintf("contacts-%v", i)
|
||||
g.deriveTree(partialContactUserKey, partialMuunUserKey, 200, branch)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *AddressGenerator) deriveTree(rootUserKey, rootMuunKey *libwallet.HDPrivateKey, count int64, name string) {
|
||||
|
||||
for i := int64(0); i <= count; i++ {
|
||||
userKey, err := rootUserKey.DerivedAt(i, false)
|
||||
if err != nil {
|
||||
log.Printf("skipping child %v for %v due to %v", i, name, err)
|
||||
continue
|
||||
}
|
||||
muunKey, err := rootMuunKey.DerivedAt(i, false)
|
||||
if err != nil {
|
||||
log.Printf("skipping child %v for %v due to %v", i, name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
addrV2, err := libwallet.CreateAddressV2(userKey.PublicKey(), muunKey.PublicKey())
|
||||
if err == nil {
|
||||
g.addrs[addrV2.Address()] = signingDetails{
|
||||
RedeemScript: addrV2.(libwallet.RedeemableAddress).RedeemScript(),
|
||||
Address: addrV2,
|
||||
}
|
||||
} else {
|
||||
log.Printf("failed to generate %v v2 for %v due to %v", name, i, err)
|
||||
}
|
||||
|
||||
addrV3, err := libwallet.CreateAddressV3(userKey.PublicKey(), muunKey.PublicKey())
|
||||
if err == nil {
|
||||
g.addrs[addrV3.Address()] = signingDetails{
|
||||
RedeemScript: addrV3.(libwallet.RedeemableAddress).RedeemScript(),
|
||||
Address: addrV3,
|
||||
}
|
||||
} else {
|
||||
log.Printf("failed to generate %v v3 for %v due to %v", name, i, err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
|
||||
"github.com/btcsuite/btcd/btcjson"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
_ "github.com/btcsuite/btcwallet/chain"
|
||||
"github.com/btcsuite/btcwallet/walletdb"
|
||||
_ "github.com/btcsuite/btcwallet/walletdb/bdb"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
|
||||
"github.com/lightninglabs/neutrino"
|
||||
"github.com/lightninglabs/neutrino/headerfs"
|
||||
)
|
||||
|
||||
// RelevantTx contains a PKScipt, an Address an a boolean to check if its spent or not
|
||||
type RelevantTx struct {
|
||||
PkScript []byte
|
||||
Address string
|
||||
Spent bool
|
||||
Satoshis int64
|
||||
SigningDetails signingDetails
|
||||
Outpoint wire.OutPoint
|
||||
}
|
||||
|
||||
func (tx *RelevantTx) String() string {
|
||||
return fmt.Sprintf("outpoint %v:%v for %v sats on path %v",
|
||||
tx.Outpoint.Hash, tx.Outpoint.Index, tx.Satoshis, tx.SigningDetails.Address.DerivationPath())
|
||||
}
|
||||
|
||||
var (
|
||||
chainParams = chaincfg.MainNetParams
|
||||
bitcoinGenesisDate = chainParams.GenesisBlock.Header.Timestamp
|
||||
)
|
||||
|
||||
var relevantTxs = make(map[wire.OutPoint]*RelevantTx)
|
||||
var rescan *neutrino.Rescan
|
||||
|
||||
// TODO: Add signing details to the watchAddresses map
|
||||
var watchAddresses = make(map[string]signingDetails)
|
||||
|
||||
func startRescan(chainService *neutrino.ChainService, addrs map[string]signingDetails, birthday int) []*RelevantTx {
|
||||
watchAddresses = addrs
|
||||
|
||||
// Wait till we know where the tip is
|
||||
for !chainService.IsCurrent() {
|
||||
}
|
||||
bestBlock, _ := chainService.BestBlock()
|
||||
|
||||
startHeight := findStartHeight(birthday, chainService)
|
||||
fmt.Println()
|
||||
fmt.Printf("Starting at height %v", startHeight.Height)
|
||||
fmt.Println()
|
||||
|
||||
ntfn := rpcclient.NotificationHandlers{
|
||||
OnBlockConnected: func(hash *chainhash.Hash, height int32, t time.Time) {
|
||||
totalDif := bestBlock.Height - startHeight.Height
|
||||
currentDif := height - startHeight.Height
|
||||
progress := (float64(currentDif) / float64(totalDif)) * 100.0
|
||||
progressBar := ""
|
||||
numberOfBars := int(progress / 5)
|
||||
for index := 0; index <= 20; index++ {
|
||||
if index <= numberOfBars {
|
||||
progressBar += "■"
|
||||
} else {
|
||||
progressBar += "□"
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\rProgress: [%v] %.2f%%. Scanning block %v of %v.", progressBar, progress, currentDif, totalDif)
|
||||
},
|
||||
OnRedeemingTx: func(tx *btcutil.Tx, details *btcjson.BlockDetails) {
|
||||
for _, input := range tx.MsgTx().TxIn {
|
||||
outpoint := input.PreviousOutPoint
|
||||
if _, ok := relevantTxs[outpoint]; ok {
|
||||
relevantTxs[outpoint].Spent = true
|
||||
}
|
||||
}
|
||||
},
|
||||
OnRecvTx: func(tx *btcutil.Tx, details *btcjson.BlockDetails) {
|
||||
checkOutpoints(tx, details.Height)
|
||||
},
|
||||
}
|
||||
|
||||
rescan = neutrino.NewRescan(
|
||||
&neutrino.RescanChainSource{
|
||||
ChainService: chainService,
|
||||
},
|
||||
neutrino.WatchAddrs(buildAddresses()...),
|
||||
neutrino.NotificationHandlers(ntfn),
|
||||
neutrino.StartBlock(startHeight),
|
||||
neutrino.EndBlock(bestBlock),
|
||||
)
|
||||
errorChan := rescan.Start()
|
||||
rescan.WaitForShutdown()
|
||||
if err := <-errorChan; err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return buildUtxos()
|
||||
}
|
||||
|
||||
func startChainService() (*neutrino.ChainService, func(), error) {
|
||||
setUpLogger()
|
||||
|
||||
dir := os.TempDir()
|
||||
dirFolder := filepath.Join(dir, "muunRecoveryTool")
|
||||
os.RemoveAll(dirFolder)
|
||||
os.MkdirAll(dirFolder, 0700)
|
||||
dbPath := filepath.Join(dirFolder, "neutrino.db")
|
||||
|
||||
db, err := walletdb.Open("bdb", dbPath)
|
||||
if err == walletdb.ErrDbDoesNotExist {
|
||||
db, err = walletdb.Create("bdb", dbPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
peers := make([]string, 1)
|
||||
peers[0] = "btcd-mainnet.lightning.computer"
|
||||
chainService, err := neutrino.NewChainService(neutrino.Config{
|
||||
DataDir: dirFolder,
|
||||
Database: db,
|
||||
ChainParams: chainParams,
|
||||
ConnectPeers: peers,
|
||||
AddPeers: peers,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = chainService.Start()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
close := func() {
|
||||
db.Close()
|
||||
err := chainService.Stop()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Remove(dbPath)
|
||||
os.RemoveAll(dirFolder)
|
||||
}
|
||||
return chainService, close, err
|
||||
}
|
||||
|
||||
func findStartHeight(birthday int, chain *neutrino.ChainService) *headerfs.BlockStamp {
|
||||
if birthday == 0 {
|
||||
return &headerfs.BlockStamp{}
|
||||
}
|
||||
|
||||
const (
|
||||
// birthdayBlockDelta is the maximum time delta allowed between our
|
||||
// birthday timestamp and our birthday block's timestamp when searching
|
||||
// for a better birthday block candidate (if possible).
|
||||
birthdayBlockDelta = 2 * time.Hour
|
||||
)
|
||||
|
||||
birthtime := bitcoinGenesisDate.Add(time.Duration(birthday-2) * 24 * time.Hour)
|
||||
|
||||
block, _ := chain.BestBlock()
|
||||
|
||||
startHeight := int32(0)
|
||||
bestHeight := block.Height
|
||||
|
||||
left, right := startHeight, bestHeight
|
||||
|
||||
for {
|
||||
mid := left + (right-left)/2
|
||||
hash, _ := chain.GetBlockHash(int64(mid))
|
||||
header, _ := chain.GetBlockHeader(hash)
|
||||
|
||||
// If the search happened to reach either of our range extremes,
|
||||
// then we'll just use that as there's nothing left to search.
|
||||
if mid == startHeight || mid == bestHeight || mid == left {
|
||||
return &headerfs.BlockStamp{
|
||||
Hash: *hash,
|
||||
Height: mid,
|
||||
Timestamp: header.Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// The block's timestamp is more than 2 hours after the
|
||||
// birthday, so look for a lower block.
|
||||
if header.Timestamp.Sub(birthtime) > birthdayBlockDelta {
|
||||
right = mid
|
||||
continue
|
||||
}
|
||||
|
||||
// The birthday is more than 2 hours before the block's
|
||||
// timestamp, so look for a higher block.
|
||||
if header.Timestamp.Sub(birthtime) < -birthdayBlockDelta {
|
||||
left = mid
|
||||
continue
|
||||
}
|
||||
|
||||
return &headerfs.BlockStamp{
|
||||
Hash: *hash,
|
||||
Height: mid,
|
||||
Timestamp: header.Timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkOutpoints(tx *btcutil.Tx, height int32) {
|
||||
// Loop in the output addresses
|
||||
for index, output := range tx.MsgTx().TxOut {
|
||||
_, addrs, _, _ := txscript.ExtractPkScriptAddrs(output.PkScript, &chainParams)
|
||||
for _, addr := range addrs {
|
||||
// If one of the output addresses is in our Watch Addresses map, we try to add it to our relevant tx model
|
||||
if _, ok := watchAddresses[addr.EncodeAddress()]; ok {
|
||||
hash := tx.Hash()
|
||||
relevantTx := &RelevantTx{
|
||||
PkScript: output.PkScript,
|
||||
Address: addr.String(),
|
||||
Spent: false,
|
||||
Satoshis: output.Value,
|
||||
SigningDetails: watchAddresses[addr.EncodeAddress()],
|
||||
Outpoint: wire.OutPoint{*hash, uint32(index)},
|
||||
}
|
||||
|
||||
if _, ok := relevantTxs[relevantTx.Outpoint]; ok {
|
||||
// If its already there we dont need to do anything
|
||||
return
|
||||
}
|
||||
|
||||
relevantTxs[relevantTx.Outpoint] = relevantTx
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildUtxos() []*RelevantTx {
|
||||
var utxos []*RelevantTx
|
||||
for _, output := range relevantTxs {
|
||||
if !output.Spent {
|
||||
utxos = append(utxos, output)
|
||||
}
|
||||
}
|
||||
return utxos
|
||||
}
|
||||
|
||||
func buildAddresses() []btcutil.Address {
|
||||
addresses := make([]btcutil.Address, 0, len(watchAddresses))
|
||||
for addr := range watchAddresses {
|
||||
address, err := btcutil.DecodeAddress(addr, &chainParams)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
addresses = append(addresses, address)
|
||||
}
|
||||
return addresses
|
||||
}
|
||||
|
||||
func setUpLogger() {
|
||||
logger := btclog.NewBackend(os.Stdout).Logger("MUUN")
|
||||
logger.SetLevel(btclog.LevelOff)
|
||||
neutrino.UseLogger(logger)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
module recovery_tool
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190911065739-d5cdeb4b91b0
|
||||
github.com/btcsuite/btcwallet/walletdb v1.0.0
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190910092203-46d9c1c55f44
|
||||
github.com/muun/libwallet v0.1.2
|
||||
)
|
||||
|
||||
replace github.com/lightninglabs/neutrino => github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257
|
|
@ -0,0 +1,221 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo=
|
||||
github.com/NebulousLabs/fastrand v0.0.0-20180208210444-3cf7173006a0/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ=
|
||||
github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
|
||||
github.com/btcsuite/btcd v0.0.0-20180823030728-d81d8877b8f3/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20181130015935-7d2daa5bfef2/go.mod h1:Jr9bmNVGZ7TH2Ux1QuP0ec+yGgh0gE9FIlkzQiI5bR0=
|
||||
github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
|
||||
github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0=
|
||||
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20180904010540-284e2e0e696e33d5be388f7f3d9a26db703e0c06/go.mod h1:/d7QHZsfUAruXuBhyPITqoYOmJ+nq35qPsJjz/aSpCg=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190313032608-acf3b04b0273/go.mod h1:mkOYY8/psBiL5E+Wb0V7M0o+N7NXi2SZJz6+RKkncIc=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190319010515-89ab2044f962/go.mod h1:qMi4jGpAO6YRsd81RYDG7o5pBIGqN9faCioJdagLu64=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190712034938-7a3a3e82cbb6/go.mod h1:sXVxjjP5YeWqWsiQbQDXvAw6J6Qvr8swu7MONoNaF9k=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190911065739-d5cdeb4b91b0 h1:S9+cnZ7N4EvkkOBQ3lUy4p7+XjW4GS81R4QjwuT06Cw=
|
||||
github.com/btcsuite/btcwallet v0.0.0-20190911065739-d5cdeb4b91b0/go.mod h1:ntLqUbZ12G8FmPX1nJj7W83WiAFOLRGiuarH4zDYdlI=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0 h1:6DxkcoMnCPY4E9cUDPB5tbuuf40SmmMkSQkoE8vCT+s=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.0.0 h1:mheT7vCWK5EP6rZzhxsQ7ms9+yX4VE8bwiJctECBeNw=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA=
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY=
|
||||
github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:QcFA8DZHtuIAdYKCq/BzELOaznRsCvwf4zTPmaYwaig=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v0.0.0-20180223184059-7ee3ded59d4835e10f3e7d0f7603c42aa5e83820/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20180821051752-b27b920f9e71/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v0.0.0-20170724004829-f2862b476edc/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
|
||||
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/juju/clock v0.0.0-20180808021310-bab88fc67299/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
|
||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/retry v0.0.0-20180821225755-9058e192b216/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4=
|
||||
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
|
||||
github.com/juju/version v0.0.0-20180108022336-b64dbd566305/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec h1:n1NeQ3SgUHyISrjFFoO5dR748Is8dBL9qpaTNfphQrs=
|
||||
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lightninglabs/gozmq v0.0.0-20180324010646-462a8a753885/go.mod h1:KUh15naRlx/TmUMFS/p4JJrCrE6F7RGF7rsnvuu45E4=
|
||||
github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d h1:tt8hwvxl6fksSfchjBGaWu+pnWJQfG1OWiCM20qOSAE=
|
||||
github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20181017011010-4d6069299130/go.mod h1:KJq43Fu9ceitbJsSXMILcT4mGDNI/crKmPIkDOZXFyM=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190213031021-ae4583a89cfb/go.mod h1:g6cMQd+hfAU8pQTJAdjm6/EQREhupyd22f+CL0qYFOE=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190313035638-e1ad4c33fb18/go.mod h1:v6tz6jbuAubTrRpX8ke2KH9sJxml8KlPQTKgo9mAp1Q=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190725230401-ddf667a8b5c4/go.mod h1:vzLU75ll8qbRJIzW5dvK/UXtR9c2FecJ6VNOM8chyVM=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190906012717-f087198de655/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190910092203-46d9c1c55f44 h1:/LCNh3QEflF49ZYjfHO8yNhtQ/U/crV5Ktj5YIIrPEA=
|
||||
github.com/lightninglabs/neutrino v0.0.0-20190910092203-46d9c1c55f44/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA=
|
||||
github.com/lightningnetwork/lightning-onion v0.0.0-20190703000913-ecc936dc56c9/go.mod h1:Sooe/CoCqa85JxqHV+IBR2HW+6t2Cv+36awSmoccswM=
|
||||
github.com/lightningnetwork/lnd v0.7.1-beta-rc2 h1:N0AuHo4wI6TogabvOfpwg1LkR3RxGCvqYq0Wb7GL+ck=
|
||||
github.com/lightningnetwork/lnd v0.7.1-beta-rc2/go.mod h1:ODASBFcJwVlb4aqO3m090whpP2kfA9zEvmG/pj+fOfg=
|
||||
github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0=
|
||||
github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
|
||||
github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU=
|
||||
github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0=
|
||||
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY=
|
||||
github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 h1:PRMAcldsl4mXKJeRNB/KVNz6TlbS6hk2Rs42PqgU3Ws=
|
||||
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/muun/libwallet v0.1.2 h1:SL19DYwRYuZ09XKcnIwYSxmFMHtMde8G5AV8LAq9rXg=
|
||||
github.com/muun/libwallet v0.1.2/go.mod h1:/ub+7bo/snLSWuwBXdpGDiY5HBuVsduYrUAW4yZ88Xc=
|
||||
github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257 h1:NW17wq2gZlEFeW3/Zx3wSmqlD0wKGf7YvhpP+CNCsbE=
|
||||
github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
|
||||
github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180821023952-922f4815f713/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
|
||||
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/macaroon-bakery.v2 v2.0.1/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA=
|
||||
gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
log "log"
|
||||
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"github.com/muun/libwallet"
|
||||
)
|
||||
|
||||
func buildExtendedKey(rawKey, recoveryCode string) *libwallet.DecryptedPrivateKey {
|
||||
recoveryCodeBytes := extractBytes(recoveryCode)
|
||||
salt := extractSalt(rawKey)
|
||||
|
||||
privKey := libwallet.NewChallengePrivateKey(recoveryCodeBytes, salt)
|
||||
|
||||
key, err := privKey.DecryptKey(rawKey, libwallet.Mainnet())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to decrypt key: %v", err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func extractSalt(rawKey string) []byte {
|
||||
bytes := base58.Decode(rawKey)
|
||||
return bytes[len(bytes)-8:]
|
||||
}
|
||||
|
||||
func extractBytes(recoveryCode string) []byte {
|
||||
return []byte(recoveryCode)
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
|
||||
"github.com/muun/libwallet"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chainService, close, _ := startChainService()
|
||||
defer close()
|
||||
|
||||
printWelcomeMessage()
|
||||
|
||||
recoveryCode := readRecoveryCode()
|
||||
|
||||
userRawKey := readKey("first encrypted private key", 147)
|
||||
userKey := buildExtendedKey(userRawKey, recoveryCode)
|
||||
userKey.Key.Path = "m/1'/1'"
|
||||
|
||||
muunRawKey := readKey("second encrypted private key", 147)
|
||||
muunKey := buildExtendedKey(muunRawKey, recoveryCode)
|
||||
derivedMuunKey, err := muunKey.Key.DeriveTo("m/1'/1'")
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
|
||||
sweepAddress := readSweepAddress()
|
||||
|
||||
fmt.Println("")
|
||||
fmt.Println("Starting to scan the blockchain. This may take a while.")
|
||||
|
||||
g := NewAddressGenerator(userKey.Key, muunKey.Key)
|
||||
g.Generate()
|
||||
|
||||
birthday := muunKey.Birthday
|
||||
if birthday == 0xFFFF {
|
||||
birthday = 0
|
||||
}
|
||||
|
||||
utxos := startRescan(chainService, g.Addresses(), birthday)
|
||||
fmt.Println("")
|
||||
|
||||
if len(utxos) > 0 {
|
||||
fmt.Printf("The recovery tool has found the following utxos: %v", utxos)
|
||||
} else {
|
||||
fmt.Printf("No utxos found")
|
||||
fmt.Println()
|
||||
return
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// This is fun:
|
||||
// First we build a sweep tx with 0 fee with the only purpouse of seeing its signed size
|
||||
zeroFeehexSweepTx := buildSweepTx(utxos, sweepAddress, 0)
|
||||
zeroFeeSweepTx, err := buildSignedTx(utxos, zeroFeehexSweepTx, userKey.Key, derivedMuunKey)
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
weightInBytes := int64(zeroFeeSweepTx.SerializeSize())
|
||||
fee := readFee(zeroFeeSweepTx.TxOut[0].Value, weightInBytes)
|
||||
// Then we re-build the sweep tx with the actual fee
|
||||
hexSweepTx := buildSweepTx(utxos, sweepAddress, fee)
|
||||
tx, err := buildSignedTx(utxos, hexSweepTx, userKey.Key, derivedMuunKey)
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
fmt.Println("Transaction ready to be sent")
|
||||
|
||||
err = chainService.SendTransaction(tx)
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Transaction sent! You can check the status here: https://blockstream.info/tx/%v", tx.TxHash().String())
|
||||
fmt.Println("")
|
||||
fmt.Printf("If you have any feedback, feel free to share it with us. Our email is contact@muun.com")
|
||||
fmt.Println("")
|
||||
|
||||
}
|
||||
|
||||
func buildSweepTx(utxos []*RelevantTx, sweepAddress btcutil.Address, fee int64) string {
|
||||
|
||||
tx := wire.NewMsgTx(2)
|
||||
value := int64(0)
|
||||
|
||||
for _, utxo := range utxos {
|
||||
tx.AddTxIn(wire.NewTxIn(&utxo.Outpoint, []byte{}, [][]byte{}))
|
||||
value += utxo.Satoshis
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Total balance in satoshis: %v", value)
|
||||
fmt.Println()
|
||||
|
||||
value -= fee
|
||||
|
||||
script, err := txscript.PayToAddrScript(sweepAddress)
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
tx.AddTxOut(wire.NewTxOut(value, script))
|
||||
|
||||
writer := &bytes.Buffer{}
|
||||
err = tx.Serialize(writer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if fee != 0 {
|
||||
readConfirmation(value, fee, sweepAddress.String())
|
||||
}
|
||||
|
||||
return hex.EncodeToString(writer.Bytes())
|
||||
}
|
||||
|
||||
func buildSignedTx(utxos []*RelevantTx, hexSweepTx string, userKey *libwallet.HDPrivateKey,
|
||||
muunKey *libwallet.HDPrivateKey) (*wire.MsgTx, error) {
|
||||
|
||||
pstx, err := libwallet.NewPartiallySignedTransaction(hexSweepTx)
|
||||
if err != nil {
|
||||
printError(err)
|
||||
}
|
||||
|
||||
for index, utxo := range utxos {
|
||||
input := &input{
|
||||
utxo,
|
||||
[]byte{},
|
||||
}
|
||||
|
||||
pstx.AddInput(input)
|
||||
sig, err := pstx.MuunSignatureForInput(index, userKey.PublicKey(), muunKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
input.muunSignature = sig
|
||||
}
|
||||
|
||||
signedTx, err := pstx.Sign(userKey, muunKey.PublicKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wireTx := wire.NewMsgTx(0)
|
||||
wireTx.BtcDecode(bytes.NewReader(signedTx.Bytes), 0, wire.WitnessEncoding)
|
||||
return wireTx, nil
|
||||
}
|
||||
|
||||
func printError(err error) {
|
||||
log.Printf("The recovery tool failed with the following error: %v", err.Error())
|
||||
log.Printf("")
|
||||
log.Printf("You can try again or contact us at support@muun.com")
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func printWelcomeMessage() {
|
||||
fmt.Println("Welcome to Muun's Recovery Tool")
|
||||
fmt.Println("")
|
||||
fmt.Println("You can use this tool to swipe all the balance in your muun account to an")
|
||||
fmt.Println("address of your choosing.")
|
||||
fmt.Println("")
|
||||
fmt.Println("To do this you will need:")
|
||||
fmt.Println("* The recovery code, that you set up when you created your muun account")
|
||||
fmt.Println("* The two encrypted private keys that you exported from your muun wallet")
|
||||
fmt.Println("* A destination bitcoin address where all your funds will be sent")
|
||||
fmt.Println("")
|
||||
fmt.Println("If you have any questions, contact us at contact@muun.com")
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func readRecoveryCode() string {
|
||||
fmt.Println("")
|
||||
fmt.Printf("Enter your Recovery Code")
|
||||
fmt.Println()
|
||||
fmt.Println("(it looks like this: 'ABCD-1234-POW2-R561-P120-JK26-12RW-45TT')")
|
||||
fmt.Print("> ")
|
||||
var userInput string
|
||||
fmt.Scan(&userInput)
|
||||
userInput = strings.TrimSpace(userInput)
|
||||
|
||||
finalRC := strings.ToUpper(userInput)
|
||||
|
||||
if strings.Count(finalRC, "-") != 7 {
|
||||
fmt.Printf("Wrong recovery code, remember to add the '-' separator between the 4 characters chunks")
|
||||
fmt.Println()
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readRecoveryCode()
|
||||
}
|
||||
|
||||
if len(finalRC) != 39 {
|
||||
fmt.Println("Your recovery code must have 39 characters")
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readRecoveryCode()
|
||||
}
|
||||
|
||||
return finalRC
|
||||
}
|
||||
|
||||
func readKey(keyType string, characters int) string {
|
||||
fmt.Println("")
|
||||
fmt.Printf("Enter your %v", keyType)
|
||||
fmt.Println()
|
||||
fmt.Println("(it looks like this: '9xzpc7y6sNtRvh8Fh...')")
|
||||
fmt.Print("> ")
|
||||
var userInput string
|
||||
fmt.Scan(&userInput)
|
||||
userInput = strings.TrimSpace(userInput)
|
||||
|
||||
if len(userInput) != characters {
|
||||
fmt.Printf("Your %v must have %v characters", keyType, characters)
|
||||
fmt.Println("")
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readKey(keyType, characters)
|
||||
}
|
||||
|
||||
return userInput
|
||||
}
|
||||
|
||||
func readSweepAddress() btcutil.Address {
|
||||
fmt.Println("")
|
||||
fmt.Println("Enter your destination bitcoin address")
|
||||
fmt.Print("> ")
|
||||
var userInput string
|
||||
fmt.Scan(&userInput)
|
||||
userInput = strings.TrimSpace(userInput)
|
||||
|
||||
addr, err := btcutil.DecodeAddress(userInput, &chainParams)
|
||||
if err != nil {
|
||||
fmt.Println("This is not a valid bitcoin address")
|
||||
fmt.Println("")
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readSweepAddress()
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func readFee(totalBalance, weight int64) int64 {
|
||||
fmt.Println("")
|
||||
fmt.Printf("Enter the fee in satoshis per byte. Tx weight: %v bytes. You can check the status of the mempool here: https://bitcoinfees.earn.com/#fees", weight)
|
||||
fmt.Println()
|
||||
fmt.Println("(Example: 5)")
|
||||
fmt.Print("> ")
|
||||
var userInput string
|
||||
fmt.Scan(&userInput)
|
||||
feeInSatsPerByte, err := strconv.ParseInt(userInput, 10, 64)
|
||||
if err != nil || feeInSatsPerByte <= 0 {
|
||||
fmt.Printf("The fee must be a number")
|
||||
fmt.Println("")
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readFee(totalBalance, weight)
|
||||
}
|
||||
|
||||
totalFee := feeInSatsPerByte * weight
|
||||
|
||||
if totalBalance-totalFee < 546 {
|
||||
fmt.Printf("The fee is too high. The amount left must be higher than dust")
|
||||
fmt.Println("")
|
||||
fmt.Println("Please, try again")
|
||||
|
||||
return readFee(totalBalance, weight)
|
||||
}
|
||||
|
||||
return totalFee
|
||||
}
|
||||
|
||||
func readConfirmation(value, fee int64, address string) {
|
||||
fmt.Println("")
|
||||
fmt.Printf("About to send %v satoshis with fee: %v satoshis to %v", value, fee, address)
|
||||
fmt.Println()
|
||||
fmt.Println("Confirm? (y/n)")
|
||||
fmt.Print("> ")
|
||||
var userInput string
|
||||
fmt.Scan(&userInput)
|
||||
|
||||
if userInput == "y" || userInput == "Y" {
|
||||
return
|
||||
}
|
||||
|
||||
if userInput == "n" || userInput == "N" {
|
||||
log.Println()
|
||||
log.Printf("Recovery tool stopped")
|
||||
log.Println()
|
||||
log.Printf("You can try again or contact us at support@muun.com")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Println("You can only enter 'y' to accept or 'n' to cancel")
|
||||
readConfirmation(value, fee, address)
|
||||
}
|
||||
|
||||
type input struct {
|
||||
tx *RelevantTx
|
||||
muunSignature []byte
|
||||
}
|
||||
|
||||
func (i *input) OutPoint() libwallet.Outpoint {
|
||||
return &outpoint{tx: i.tx}
|
||||
}
|
||||
|
||||
func (i *input) Address() libwallet.MuunAddress {
|
||||
return i.tx.SigningDetails.Address
|
||||
}
|
||||
|
||||
func (i *input) UserSignature() []byte {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func (i *input) MuunSignature() []byte {
|
||||
return i.muunSignature
|
||||
}
|
||||
|
||||
func (i *input) SubmarineSwap() libwallet.InputSubmarineSwap {
|
||||
return nil
|
||||
}
|
||||
|
||||
type outpoint struct {
|
||||
tx *RelevantTx
|
||||
}
|
||||
|
||||
func (o *outpoint) TxId() []byte {
|
||||
return o.tx.Outpoint.Hash.CloneBytes()
|
||||
}
|
||||
|
||||
func (o *outpoint) Index() int {
|
||||
return int(o.tx.Outpoint.Index)
|
||||
}
|
||||
|
||||
func (o *outpoint) Amount() int64 {
|
||||
return o.tx.Satoshis
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
.vscode
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
|
@ -0,0 +1,15 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- "1.6.x"
|
||||
- "1.7.x"
|
||||
- "1.8.x"
|
||||
- "1.9.x"
|
||||
- "1.10.x"
|
||||
|
||||
env:
|
||||
- TRAVIS_GOARCH=amd64
|
||||
- TRAVIS_GOARCH=386
|
||||
|
||||
before_install:
|
||||
- export GOARCH=$TRAVIS_GOARCH
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Andreas Auernhammer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,55 @@
|
|||
[![Godoc Reference](https://godoc.org/github.com/aead/siphash?status.svg)](https://godoc.org/github.com/aead/siphash)
|
||||
[![Build Status](https://travis-ci.org/aead/siphash.svg?branch=master)](https://travis-ci.org/aead/siphash)
|
||||
|
||||
## The SipHash pseudo-random function
|
||||
|
||||
SipHash is a family of pseudo-random functions (a.k.a. keyed hash functions) optimized for speed on short messages.
|
||||
SipHash computes a 64-bit or 128 bit message authentication code from a variable-length message and 128-bit secret key.
|
||||
This implementation uses the recommended parameters c=2 and d=4.
|
||||
|
||||
### Installation
|
||||
Install in your GOPATH: `go get -u github.com/aead/siphash`
|
||||
|
||||
### Performance
|
||||
**AMD64**
|
||||
Hardware: Intel i7-6500U 2.50GHz x 2
|
||||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-67-generic
|
||||
Go version: 1.8.0
|
||||
```
|
||||
name speed cpb
|
||||
Write_8-4 688MB/s ± 0% 3.47
|
||||
Write_1K-4 2.09GB/s ± 5% 1.11
|
||||
Sum64_8-4 244MB/s ± 1% 9.77
|
||||
Sum64_1K-4 2.06GB/s ± 0% 1.13
|
||||
Sum128_8-4 189MB/s ± 0% 12.62
|
||||
Sum128_1K-4 2.03GB/s ± 0% 1.15
|
||||
```
|
||||
|
||||
**386**
|
||||
Hardware: Intel i7-6500U 2.50GHz x 2 - SSE2 SIMD
|
||||
System: Linux Ubuntu 16.04 - kernel: 4.4.0-67-generic
|
||||
Go version: 1.8.0
|
||||
```
|
||||
name speed cpb
|
||||
Write_8-4 434MB/s ± 2% 5.44
|
||||
Write_1K-4 1.24GB/s ± 1% 1.88
|
||||
Sum64_8-4 92.6MB/s ± 4% 25.92
|
||||
Sum64_1K-4 1.15GB/s ± 1% 2.03
|
||||
Sum128_8-4 61.5MB/s ± 5% 39.09
|
||||
Sum128_1K-4 1.10GB/s ± 0% 2.12
|
||||
```
|
||||
|
||||
**ARM**
|
||||
Hardware: ARM-Cortex-A7 (ARMv7) 1GHz (912MHz) x 2
|
||||
System: Linux Ubuntu 14.04.1 - kernel: 3.4.112-sun7i
|
||||
Go version: 1.7.4
|
||||
|
||||
```
|
||||
name speed cpb
|
||||
Write_8-2 43.4MB/s ± 2% 21.97
|
||||
Write_1K-2 125MB/s ± 1% 7.63
|
||||
Sum64_8-2 6.51MB/s ± 1% 146.49
|
||||
Sum64_1K-2 111MB/s ± 1% 8.59
|
||||
Sum128_8-2 3.82MB/s ± 2% 249.65
|
||||
Sum128_1K-2 101MB/s ± 1% 9.44
|
||||
```
|
|
@ -0,0 +1,157 @@
|
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Package siphash implements the SipHash-64 and SipHash-128
|
||||
// pseudo-random-functions - with the recommended parameters:
|
||||
// c = 2 and d = 4.
|
||||
// SipHash computes a message authentication code (MAC) from a
|
||||
// variable-length message and a 128 bit secret key. SipHash
|
||||
// was designed to be efficient, even for short inputs, with
|
||||
// performance comparable to non-cryptographic hash functions.
|
||||
//
|
||||
//
|
||||
// Security
|
||||
//
|
||||
// SipHash cannot be used as a cryptographic hash function.
|
||||
// Neither SipHash-64 nor SipHash-128 are strong collision
|
||||
// resistant.
|
||||
//
|
||||
//
|
||||
// Recommendations
|
||||
//
|
||||
// SipHash was designed to defend hash flooding DoS attacks.
|
||||
// SipHash-64 can be used as hashing scheme within hash maps
|
||||
// or other key-value data structures.
|
||||
// SipHash-128 can be used to compute a 128 bit authentication
|
||||
// tag for messages.
|
||||
package siphash // import "github.com/aead/siphash"
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is the size of the SipHash secret key in bytes.
|
||||
KeySize = 16
|
||||
// BlockSize is the block size of SipHash in bytes.
|
||||
BlockSize = 8
|
||||
)
|
||||
|
||||
const (
|
||||
c0 = 0x736f6d6570736575
|
||||
c1 = 0x646f72616e646f6d
|
||||
c2 = 0x6c7967656e657261
|
||||
c3 = 0x7465646279746573
|
||||
)
|
||||
|
||||
type KeySizeError uint
|
||||
|
||||
func (k KeySizeError) Error() string {
|
||||
return "siphash: invalid key size " + strconv.Itoa(int(k))
|
||||
}
|
||||
|
||||
// Sum64 returns the 64 bit authenticator for msg using a 128 bit secret key.
|
||||
func Sum64(msg []byte, key *[KeySize]byte) uint64 {
|
||||
k0 := binary.LittleEndian.Uint64(key[0:])
|
||||
k1 := binary.LittleEndian.Uint64(key[8:])
|
||||
|
||||
var hVal [4]uint64
|
||||
hVal[0] = k0 ^ c0
|
||||
hVal[1] = k1 ^ c1
|
||||
hVal[2] = k0 ^ c2
|
||||
hVal[3] = k1 ^ c3
|
||||
|
||||
n := len(msg)
|
||||
ctr := byte(n)
|
||||
|
||||
if n >= BlockSize {
|
||||
n &= (^(BlockSize - 1))
|
||||
core(&hVal, msg[:n])
|
||||
msg = msg[n:]
|
||||
}
|
||||
|
||||
var block [BlockSize]byte
|
||||
copy(block[:], msg)
|
||||
block[7] = ctr
|
||||
|
||||
return finalize64(&hVal, &block)
|
||||
}
|
||||
|
||||
// New64 returns a hash.Hash64 computing the SipHash-64 checksum.
|
||||
// This function returns a non-nil error if len(key) != 16.
|
||||
func New64(key []byte) (hash.Hash64, error) {
|
||||
if k := len(key); k != KeySize {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
h := new(digest64)
|
||||
h.key[0] = binary.LittleEndian.Uint64(key)
|
||||
h.key[1] = binary.LittleEndian.Uint64(key[8:])
|
||||
h.Reset()
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type digest64 struct {
|
||||
hVal [4]uint64
|
||||
key [2]uint64
|
||||
block [BlockSize]byte
|
||||
off int
|
||||
ctr byte
|
||||
}
|
||||
|
||||
func (d *digest64) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest64) Size() int { return 8 }
|
||||
|
||||
func (d *digest64) Reset() {
|
||||
d.hVal[0] = d.key[0] ^ c0
|
||||
d.hVal[1] = d.key[1] ^ c1
|
||||
d.hVal[2] = d.key[0] ^ c2
|
||||
d.hVal[3] = d.key[1] ^ c3
|
||||
|
||||
d.off = 0
|
||||
d.ctr = 0
|
||||
}
|
||||
|
||||
func (d *digest64) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
d.ctr += byte(n)
|
||||
|
||||
if d.off > 0 {
|
||||
dif := BlockSize - d.off
|
||||
if n < dif {
|
||||
d.off += copy(d.block[d.off:], p)
|
||||
return
|
||||
}
|
||||
copy(d.block[d.off:], p[:dif])
|
||||
core(&(d.hVal), d.block[:])
|
||||
p = p[dif:]
|
||||
d.off = 0
|
||||
}
|
||||
if nn := len(p) &^ (BlockSize - 1); nn >= BlockSize {
|
||||
core(&(d.hVal), p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.off = copy(d.block[:], p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *digest64) Sum64() uint64 {
|
||||
hVal := d.hVal
|
||||
block := d.block
|
||||
for i := d.off; i < BlockSize-1; i++ {
|
||||
block[i] = 0
|
||||
}
|
||||
block[7] = d.ctr
|
||||
return finalize64(&hVal, &block)
|
||||
}
|
||||
|
||||
func (d *digest64) Sum(sum []byte) []byte {
|
||||
var out [8]byte
|
||||
binary.LittleEndian.PutUint64(out[:], d.Sum64())
|
||||
return append(sum, out[:]...)
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package siphash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Sum128 returns the 128 bit authenticator for msg using a 128 bit secret key.
|
||||
func Sum128(msg []byte, key *[KeySize]byte) [16]byte {
|
||||
k0 := binary.LittleEndian.Uint64(key[0:])
|
||||
k1 := binary.LittleEndian.Uint64(key[8:])
|
||||
|
||||
var hVal [4]uint64
|
||||
hVal[0] = k0 ^ c0
|
||||
hVal[1] = k1 ^ c1 ^ 0xee
|
||||
hVal[2] = k0 ^ c2
|
||||
hVal[3] = k1 ^ c3
|
||||
|
||||
n := len(msg)
|
||||
ctr := byte(n)
|
||||
|
||||
if n >= BlockSize {
|
||||
n &= (^(BlockSize - 1))
|
||||
core(&hVal, msg[:n])
|
||||
msg = msg[n:]
|
||||
}
|
||||
|
||||
var block [BlockSize]byte
|
||||
copy(block[:], msg)
|
||||
block[7] = ctr
|
||||
|
||||
var out [16]byte
|
||||
finalize128(&out, &hVal, &block)
|
||||
return out
|
||||
}
|
||||
|
||||
// New128 returns a hash.Hash computing the SipHash-128 checksum.
|
||||
// This function returns a non-nil error if len(key) != 16.
|
||||
func New128(key []byte) (hash.Hash, error) {
|
||||
if k := len(key); k != KeySize {
|
||||
return nil, KeySizeError(k)
|
||||
}
|
||||
h := new(digest128)
|
||||
h.key[0] = binary.LittleEndian.Uint64(key)
|
||||
h.key[1] = binary.LittleEndian.Uint64(key[8:])
|
||||
h.Reset()
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type digest128 digest64
|
||||
|
||||
func (d *digest128) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest128) Size() int { return 16 }
|
||||
|
||||
func (d *digest128) Reset() {
|
||||
d.hVal[0] = d.key[0] ^ c0
|
||||
d.hVal[1] = d.key[1] ^ c1 ^ 0xee
|
||||
d.hVal[2] = d.key[0] ^ c2
|
||||
d.hVal[3] = d.key[1] ^ c3
|
||||
|
||||
d.off = 0
|
||||
d.ctr = 0
|
||||
}
|
||||
|
||||
func (d *digest128) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
d.ctr += byte(n)
|
||||
|
||||
if d.off > 0 {
|
||||
dif := BlockSize - d.off
|
||||
if n < dif {
|
||||
d.off += copy(d.block[d.off:], p)
|
||||
return
|
||||
}
|
||||
copy(d.block[d.off:], p[:dif])
|
||||
core(&(d.hVal), d.block[:])
|
||||
p = p[dif:]
|
||||
d.off = 0
|
||||
}
|
||||
if nn := len(p) &^ (BlockSize - 1); nn >= BlockSize {
|
||||
core(&(d.hVal), p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.off = copy(d.block[:], p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *digest128) Sum(sum []byte) []byte {
|
||||
hVal := d.hVal
|
||||
block := d.block
|
||||
for i := d.off; i < BlockSize-1; i++ {
|
||||
block[i] = 0
|
||||
}
|
||||
block[7] = d.ctr
|
||||
|
||||
var out [16]byte
|
||||
finalize128(&out, &hVal, &block)
|
||||
return append(sum, out[:]...)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386, !gccgo, !appengine
|
||||
|
||||
package siphash
|
||||
|
||||
var useSSE2 = supportsSSE2()
|
||||
|
||||
//go:noescape
|
||||
func supportsSSE2() bool
|
||||
|
||||
//go:noescape
|
||||
func coreSSE2(hVal *[4]uint64, msg []byte)
|
||||
|
||||
func core(hVal *[4]uint64, msg []byte) {
|
||||
if useSSE2 {
|
||||
coreSSE2(hVal, msg)
|
||||
} else {
|
||||
genericCore(hVal, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func finalize64(hVal *[4]uint64, block *[BlockSize]byte) uint64 {
|
||||
return genericFinalize64(hVal, block)
|
||||
}
|
||||
|
||||
func finalize128(tag *[16]byte, hVal *[4]uint64, block *[BlockSize]byte) {
|
||||
genericFinalize128(tag, hVal, block)
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build 386, !gccgo, !appengine
|
||||
|
||||
#define ROTL(n, t, v) \
|
||||
MOVO v, t; \
|
||||
PSLLQ $n, t; \
|
||||
PSRLQ $(64-n), v; \
|
||||
PXOR t, v
|
||||
|
||||
#define ROUND(v0, v1, v2, v3, t0, t1) \
|
||||
PADDQ v1, v0; \
|
||||
PADDQ v3, v2; \
|
||||
ROTL(13, t0, v1); \
|
||||
ROTL(16, t1, v3); \
|
||||
PXOR v0, v1; \
|
||||
PXOR v2, v3; \
|
||||
PSHUFD $0xE1, v0, v0; \
|
||||
PADDQ v1, v2; \
|
||||
PADDQ v3, v0; \
|
||||
ROTL(17, t0, v1); \
|
||||
ROTL(21, t1, v3); \
|
||||
PXOR v2, v1; \
|
||||
PXOR v0, v3; \
|
||||
PSHUFD $0xE1, v2, v2
|
||||
|
||||
// coreSSE2(hVal *[4]uint64, msg []byte)
|
||||
TEXT ·coreSSE2(SB), 4, $0-16
|
||||
MOVL hVal+0(FP), AX
|
||||
MOVL msg_base+4(FP), SI
|
||||
MOVL msg_len+8(FP), BX
|
||||
MOVQ 0(AX), X0
|
||||
MOVQ 8(AX), X1
|
||||
MOVQ 16(AX), X2
|
||||
MOVQ 24(AX), X3
|
||||
PXOR X6, X6
|
||||
ANDL $-8, BX
|
||||
|
||||
loop:
|
||||
MOVQ 0(SI), X6
|
||||
PXOR X6, X3
|
||||
ROUND(X0, X1, X2, X3, X4, X5)
|
||||
ROUND(X0, X1, X2, X3, X4, X5)
|
||||
PXOR X6, X0
|
||||
|
||||
LEAL 8(SI), SI
|
||||
SUBL $8, BX
|
||||
JNZ loop
|
||||
|
||||
MOVQ X0, 0(AX)
|
||||
MOVQ X1, 8(AX)
|
||||
MOVQ X2, 16(AX)
|
||||
MOVQ X3, 24(AX)
|
||||
RET
|
||||
|
||||
// func supportsSSE2() bool
|
||||
TEXT ·supportsSSE2(SB), 4, $0-1
|
||||
MOVL $1, AX
|
||||
CPUID
|
||||
SHRL $26, DX
|
||||
ANDL $1, DX // DX != 0 if support SSE2
|
||||
MOVB DX, ret+0(FP)
|
||||
RET
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build amd64, !gccgo, !appengine
|
||||
|
||||
package siphash
|
||||
|
||||
//go:noescape
|
||||
func core(hVal *[4]uint64, msg []byte)
|
||||
|
||||
func finalize64(hVal *[4]uint64, block *[BlockSize]byte) uint64 {
|
||||
return genericFinalize64(hVal, block)
|
||||
}
|
||||
|
||||
func finalize128(tag *[16]byte, hVal *[4]uint64, block *[BlockSize]byte) {
|
||||
genericFinalize128(tag, hVal, block)
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build amd64, !gccgo, !appengine
|
||||
|
||||
#define ROUND(v0, v1, v2, v3) \
|
||||
ADDQ v1, v0; \
|
||||
ADDQ v3, v2; \
|
||||
ROLQ $13, v1; \
|
||||
ROLQ $16, v3; \
|
||||
XORQ v0, v1; \
|
||||
XORQ v2, v3; \
|
||||
ROLQ $32, v0; \
|
||||
ADDQ v1, v2; \
|
||||
ADDQ v3, v0; \
|
||||
ROLQ $17, v1; \
|
||||
ROLQ $21, v3; \
|
||||
XORQ v2, v1; \
|
||||
XORQ v0, v3; \
|
||||
ROLQ $32, v2
|
||||
|
||||
// core(hVal *[4]uint64, msg []byte)
|
||||
TEXT ·core(SB), 4, $0-32
|
||||
MOVQ hVal+0(FP), AX
|
||||
MOVQ msg_base+8(FP), SI
|
||||
MOVQ msg_len+16(FP), BX
|
||||
MOVQ 0(AX), R9
|
||||
MOVQ 8(AX), R10
|
||||
MOVQ 16(AX), R11
|
||||
MOVQ 24(AX), R12
|
||||
ANDQ $-8, BX
|
||||
|
||||
loop:
|
||||
MOVQ 0(SI), DX
|
||||
XORQ DX, R12
|
||||
ROUND(R9, R10, R11, R12)
|
||||
ROUND(R9, R10, R11, R12)
|
||||
XORQ DX, R9
|
||||
|
||||
LEAQ 8(SI), SI
|
||||
SUBQ $8, BX
|
||||
JNZ loop
|
||||
|
||||
MOVQ R9, 0(AX)
|
||||
MOVQ R10, 8(AX)
|
||||
MOVQ R11, 16(AX)
|
||||
MOVQ R12, 24(AX)
|
||||
RET
|
|
@ -0,0 +1,188 @@
|
|||
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package siphash
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
func genericCore(hVal *[4]uint64, msg []byte) {
|
||||
v0, v1, v2, v3 := hVal[0], hVal[1], hVal[2], hVal[3]
|
||||
|
||||
for len(msg) > 0 {
|
||||
m := binary.LittleEndian.Uint64(msg)
|
||||
msg = msg[BlockSize:]
|
||||
|
||||
v3 ^= m
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
}
|
||||
|
||||
hVal[0], hVal[1], hVal[2], hVal[3] = v0, v1, v2, v3
|
||||
}
|
||||
|
||||
func genericFinalize64(hVal *[4]uint64, block *[BlockSize]byte) uint64 {
|
||||
v0, v1, v2, v3 := hVal[0], hVal[1], hVal[2], hVal[3]
|
||||
|
||||
m := binary.LittleEndian.Uint64(block[:])
|
||||
v3 ^= m
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
|
||||
v2 ^= 0xff
|
||||
for i := 0; i < 4; i++ {
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
}
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
}
|
||||
|
||||
func genericFinalize128(tag *[16]byte, hVal *[4]uint64, block *[BlockSize]byte) {
|
||||
v0, v1, v2, v3 := hVal[0], hVal[1], hVal[2], hVal[3]
|
||||
|
||||
m := binary.LittleEndian.Uint64(block[:])
|
||||
|
||||
v3 ^= m
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
|
||||
v0 ^= m
|
||||
|
||||
v2 ^= 0xee
|
||||
for i := 0; i < 4; i++ {
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
}
|
||||
binary.LittleEndian.PutUint64(tag[:], v0^v1^v2^v3)
|
||||
|
||||
v1 ^= 0xdd
|
||||
for i := 0; i < 4; i++ {
|
||||
v0 += v1
|
||||
v1 = v1<<13 | v1>>(64-13)
|
||||
v1 ^= v0
|
||||
v0 = v0<<32 | v0>>(64-32)
|
||||
v2 += v3
|
||||
v3 = v3<<16 | v3>>(64-16)
|
||||
v3 ^= v2
|
||||
v0 += v3
|
||||
v3 = v3<<21 | v3>>(64-21)
|
||||
v3 ^= v0
|
||||
v2 += v1
|
||||
v1 = v1<<17 | v1>>(64-17)
|
||||
v1 ^= v2
|
||||
v2 = v2<<32 | v2>>(64-32)
|
||||
}
|
||||
binary.LittleEndian.PutUint64(tag[8:], v0^v1^v2^v3)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
|
||||
// Use of this source code is governed by a license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!386 gccgo appengine nacl
|
||||
|
||||
package siphash
|
||||
|
||||
func core(hVal *[4]uint64, msg []byte) {
|
||||
genericCore(hVal, msg)
|
||||
}
|
||||
|
||||
func finalize64(hVal *[4]uint64, block *[BlockSize]byte) uint64 {
|
||||
return genericFinalize64(hVal, block)
|
||||
}
|
||||
|
||||
func finalize128(tag *[16]byte, hVal *[4]uint64, block *[BlockSize]byte) {
|
||||
genericFinalize128(tag, hVal, block)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2013-2017 The btcsuite developers
|
||||
Copyright (c) 2015-2016 The Decred developers
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script uses gocov to generate a test coverage report.
|
||||
# The gocov tool my be obtained with the following command:
|
||||
# go get github.com/axw/gocov/gocov
|
||||
#
|
||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
||||
|
||||
# Check for gocov.
|
||||
type gocov >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo >&2 "This script requires the gocov tool."
|
||||
echo >&2 "You may obtain it with the following command:"
|
||||
echo >&2 "go get github.com/axw/gocov/gocov"
|
||||
exit 1
|
||||
fi
|
||||
gocov test | gocov report
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package addrmgr implements concurrency safe Bitcoin address manager.
|
||||
|
||||
Address Manager Overview
|
||||
|
||||
In order maintain the peer-to-peer Bitcoin network, there needs to be a source
|
||||
of addresses to connect to as nodes come and go. The Bitcoin protocol provides
|
||||
the getaddr and addr messages to allow peers to communicate known addresses with
|
||||
each other. However, there needs to a mechanism to store those results and
|
||||
select peers from them. It is also important to note that remote peers can't
|
||||
be trusted to send valid peers nor attempt to provide you with only peers they
|
||||
control with malicious intent.
|
||||
|
||||
With that in mind, this package provides a concurrency safe address manager for
|
||||
caching and selecting peers in a non-deterministic manner. The general idea is
|
||||
the caller adds addresses to the address manager and notifies it when addresses
|
||||
are connected, known good, and attempted. The caller also requests addresses as
|
||||
it needs them.
|
||||
|
||||
The address manager internally segregates the addresses into groups and
|
||||
non-deterministically selects groups in a cryptographically random manner. This
|
||||
reduce the chances multiple addresses from the same nets are selected which
|
||||
generally helps provide greater peer diversity, and perhaps more importantly,
|
||||
drastically reduces the chances an attacker is able to coerce your peer into
|
||||
only connecting to nodes they control.
|
||||
|
||||
The address manager also understands routability and Tor addresses and tries
|
||||
hard to only return routable addresses. In addition, it uses the information
|
||||
provided by the caller about connected, known good, and attempted addresses to
|
||||
periodically purge peers which no longer appear to be good peers as well as
|
||||
bias the selection toward known good peers. The general idea is to make a best
|
||||
effort at only providing usable addresses.
|
||||
*/
|
||||
package addrmgr
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package addrmgr
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// KnownAddress tracks information about a known network address that is used
|
||||
// to determine how viable an address is.
|
||||
type KnownAddress struct {
|
||||
na *wire.NetAddress
|
||||
srcAddr *wire.NetAddress
|
||||
attempts int
|
||||
lastattempt time.Time
|
||||
lastsuccess time.Time
|
||||
tried bool
|
||||
refs int // reference count of new buckets
|
||||
}
|
||||
|
||||
// NetAddress returns the underlying wire.NetAddress associated with the
|
||||
// known address.
|
||||
func (ka *KnownAddress) NetAddress() *wire.NetAddress {
|
||||
return ka.na
|
||||
}
|
||||
|
||||
// LastAttempt returns the last time the known address was attempted.
|
||||
func (ka *KnownAddress) LastAttempt() time.Time {
|
||||
return ka.lastattempt
|
||||
}
|
||||
|
||||
// Services returns the services supported by the peer with the known address.
|
||||
func (ka *KnownAddress) Services() wire.ServiceFlag {
|
||||
return ka.na.Services
|
||||
}
|
||||
|
||||
// chance returns the selection probability for a known address. The priority
|
||||
// depends upon how recently the address has been seen, how recently it was last
|
||||
// attempted and how often attempts to connect to it have failed.
|
||||
func (ka *KnownAddress) chance() float64 {
|
||||
now := time.Now()
|
||||
lastAttempt := now.Sub(ka.lastattempt)
|
||||
|
||||
if lastAttempt < 0 {
|
||||
lastAttempt = 0
|
||||
}
|
||||
|
||||
c := 1.0
|
||||
|
||||
// Very recent attempts are less likely to be retried.
|
||||
if lastAttempt < 10*time.Minute {
|
||||
c *= 0.01
|
||||
}
|
||||
|
||||
// Failed attempts deprioritise.
|
||||
for i := ka.attempts; i > 0; i-- {
|
||||
c /= 1.5
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// isBad returns true if the address in question has not been tried in the last
|
||||
// minute and meets one of the following criteria:
|
||||
// 1) It claims to be from the future
|
||||
// 2) It hasn't been seen in over a month
|
||||
// 3) It has failed at least three times and never succeeded
|
||||
// 4) It has failed ten times in the last week
|
||||
// All addresses that meet these criteria are assumed to be worthless and not
|
||||
// worth keeping hold of.
|
||||
func (ka *KnownAddress) isBad() bool {
|
||||
if ka.lastattempt.After(time.Now().Add(-1 * time.Minute)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// From the future?
|
||||
if ka.na.Timestamp.After(time.Now().Add(10 * time.Minute)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Over a month old?
|
||||
if ka.na.Timestamp.Before(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Never succeeded?
|
||||
if ka.lastsuccess.IsZero() && ka.attempts >= numRetries {
|
||||
return true
|
||||
}
|
||||
|
||||
// Hasn't succeeded in too long?
|
||||
if !ka.lastsuccess.After(time.Now().Add(-1*minBadDays*time.Hour*24)) &&
|
||||
ka.attempts >= maxFailures {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package addrmgr
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
DisableLog()
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until either UseLogger or SetLogWriter are called.
|
||||
func DisableLog() {
|
||||
log = btclog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package addrmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
var (
|
||||
// rfc1918Nets specifies the IPv4 private address blocks as defined by
|
||||
// by RFC1918 (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16).
|
||||
rfc1918Nets = []net.IPNet{
|
||||
ipNet("10.0.0.0", 8, 32),
|
||||
ipNet("172.16.0.0", 12, 32),
|
||||
ipNet("192.168.0.0", 16, 32),
|
||||
}
|
||||
|
||||
// rfc2544Net specifies the the IPv4 block as defined by RFC2544
|
||||
// (198.18.0.0/15)
|
||||
rfc2544Net = ipNet("198.18.0.0", 15, 32)
|
||||
|
||||
// rfc3849Net specifies the IPv6 documentation address block as defined
|
||||
// by RFC3849 (2001:DB8::/32).
|
||||
rfc3849Net = ipNet("2001:DB8::", 32, 128)
|
||||
|
||||
// rfc3927Net specifies the IPv4 auto configuration address block as
|
||||
// defined by RFC3927 (169.254.0.0/16).
|
||||
rfc3927Net = ipNet("169.254.0.0", 16, 32)
|
||||
|
||||
// rfc3964Net specifies the IPv6 to IPv4 encapsulation address block as
|
||||
// defined by RFC3964 (2002::/16).
|
||||
rfc3964Net = ipNet("2002::", 16, 128)
|
||||
|
||||
// rfc4193Net specifies the IPv6 unique local address block as defined
|
||||
// by RFC4193 (FC00::/7).
|
||||
rfc4193Net = ipNet("FC00::", 7, 128)
|
||||
|
||||
// rfc4380Net specifies the IPv6 teredo tunneling over UDP address block
|
||||
// as defined by RFC4380 (2001::/32).
|
||||
rfc4380Net = ipNet("2001::", 32, 128)
|
||||
|
||||
// rfc4843Net specifies the IPv6 ORCHID address block as defined by
|
||||
// RFC4843 (2001:10::/28).
|
||||
rfc4843Net = ipNet("2001:10::", 28, 128)
|
||||
|
||||
// rfc4862Net specifies the IPv6 stateless address autoconfiguration
|
||||
// address block as defined by RFC4862 (FE80::/64).
|
||||
rfc4862Net = ipNet("FE80::", 64, 128)
|
||||
|
||||
// rfc5737Net specifies the IPv4 documentation address blocks as defined
|
||||
// by RFC5737 (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
|
||||
rfc5737Net = []net.IPNet{
|
||||
ipNet("192.0.2.0", 24, 32),
|
||||
ipNet("198.51.100.0", 24, 32),
|
||||
ipNet("203.0.113.0", 24, 32),
|
||||
}
|
||||
|
||||
// rfc6052Net specifies the IPv6 well-known prefix address block as
|
||||
// defined by RFC6052 (64:FF9B::/96).
|
||||
rfc6052Net = ipNet("64:FF9B::", 96, 128)
|
||||
|
||||
// rfc6145Net specifies the IPv6 to IPv4 translated address range as
|
||||
// defined by RFC6145 (::FFFF:0:0:0/96).
|
||||
rfc6145Net = ipNet("::FFFF:0:0:0", 96, 128)
|
||||
|
||||
// rfc6598Net specifies the IPv4 block as defined by RFC6598 (100.64.0.0/10)
|
||||
rfc6598Net = ipNet("100.64.0.0", 10, 32)
|
||||
|
||||
// onionCatNet defines the IPv6 address block used to support Tor.
|
||||
// bitcoind encodes a .onion address as a 16 byte number by decoding the
|
||||
// address prior to the .onion (i.e. the key hash) base32 into a ten
|
||||
// byte number. It then stores the first 6 bytes of the address as
|
||||
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43.
|
||||
//
|
||||
// This is the same range used by OnionCat, which is part part of the
|
||||
// RFC4193 unique local IPv6 range.
|
||||
//
|
||||
// In summary the format is:
|
||||
// { magic 6 bytes, 10 bytes base32 decode of key hash }
|
||||
onionCatNet = ipNet("fd87:d87e:eb43::", 48, 128)
|
||||
|
||||
// zero4Net defines the IPv4 address block for address staring with 0
|
||||
// (0.0.0.0/8).
|
||||
zero4Net = ipNet("0.0.0.0", 8, 32)
|
||||
|
||||
// heNet defines the Hurricane Electric IPv6 address block.
|
||||
heNet = ipNet("2001:470::", 32, 128)
|
||||
)
|
||||
|
||||
// ipNet returns a net.IPNet struct given the passed IP address string, number
|
||||
// of one bits to include at the start of the mask, and the total number of bits
|
||||
// for the mask.
|
||||
func ipNet(ip string, ones, bits int) net.IPNet {
|
||||
return net.IPNet{IP: net.ParseIP(ip), Mask: net.CIDRMask(ones, bits)}
|
||||
}
|
||||
|
||||
// IsIPv4 returns whether or not the given address is an IPv4 address.
|
||||
func IsIPv4(na *wire.NetAddress) bool {
|
||||
return na.IP.To4() != nil
|
||||
}
|
||||
|
||||
// IsLocal returns whether or not the given address is a local address.
|
||||
func IsLocal(na *wire.NetAddress) bool {
|
||||
return na.IP.IsLoopback() || zero4Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsOnionCatTor returns whether or not the passed address is in the IPv6 range
|
||||
// used by bitcoin to support Tor (fd87:d87e:eb43::/48). Note that this range
|
||||
// is the same range used by OnionCat, which is part of the RFC4193 unique local
|
||||
// IPv6 range.
|
||||
func IsOnionCatTor(na *wire.NetAddress) bool {
|
||||
return onionCatNet.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC1918 returns whether or not the passed address is part of the IPv4
|
||||
// private network address space as defined by RFC1918 (10.0.0.0/8,
|
||||
// 172.16.0.0/12, or 192.168.0.0/16).
|
||||
func IsRFC1918(na *wire.NetAddress) bool {
|
||||
for _, rfc := range rfc1918Nets {
|
||||
if rfc.Contains(na.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRFC2544 returns whether or not the passed address is part of the IPv4
|
||||
// address space as defined by RFC2544 (198.18.0.0/15)
|
||||
func IsRFC2544(na *wire.NetAddress) bool {
|
||||
return rfc2544Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC3849 returns whether or not the passed address is part of the IPv6
|
||||
// documentation range as defined by RFC3849 (2001:DB8::/32).
|
||||
func IsRFC3849(na *wire.NetAddress) bool {
|
||||
return rfc3849Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC3927 returns whether or not the passed address is part of the IPv4
|
||||
// autoconfiguration range as defined by RFC3927 (169.254.0.0/16).
|
||||
func IsRFC3927(na *wire.NetAddress) bool {
|
||||
return rfc3927Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC3964 returns whether or not the passed address is part of the IPv6 to
|
||||
// IPv4 encapsulation range as defined by RFC3964 (2002::/16).
|
||||
func IsRFC3964(na *wire.NetAddress) bool {
|
||||
return rfc3964Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC4193 returns whether or not the passed address is part of the IPv6
|
||||
// unique local range as defined by RFC4193 (FC00::/7).
|
||||
func IsRFC4193(na *wire.NetAddress) bool {
|
||||
return rfc4193Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC4380 returns whether or not the passed address is part of the IPv6
|
||||
// teredo tunneling over UDP range as defined by RFC4380 (2001::/32).
|
||||
func IsRFC4380(na *wire.NetAddress) bool {
|
||||
return rfc4380Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC4843 returns whether or not the passed address is part of the IPv6
|
||||
// ORCHID range as defined by RFC4843 (2001:10::/28).
|
||||
func IsRFC4843(na *wire.NetAddress) bool {
|
||||
return rfc4843Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC4862 returns whether or not the passed address is part of the IPv6
|
||||
// stateless address autoconfiguration range as defined by RFC4862 (FE80::/64).
|
||||
func IsRFC4862(na *wire.NetAddress) bool {
|
||||
return rfc4862Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC5737 returns whether or not the passed address is part of the IPv4
|
||||
// documentation address space as defined by RFC5737 (192.0.2.0/24,
|
||||
// 198.51.100.0/24, 203.0.113.0/24)
|
||||
func IsRFC5737(na *wire.NetAddress) bool {
|
||||
for _, rfc := range rfc5737Net {
|
||||
if rfc.Contains(na.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRFC6052 returns whether or not the passed address is part of the IPv6
|
||||
// well-known prefix range as defined by RFC6052 (64:FF9B::/96).
|
||||
func IsRFC6052(na *wire.NetAddress) bool {
|
||||
return rfc6052Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC6145 returns whether or not the passed address is part of the IPv6 to
|
||||
// IPv4 translated address range as defined by RFC6145 (::FFFF:0:0:0/96).
|
||||
func IsRFC6145(na *wire.NetAddress) bool {
|
||||
return rfc6145Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsRFC6598 returns whether or not the passed address is part of the IPv4
|
||||
// shared address space specified by RFC6598 (100.64.0.0/10)
|
||||
func IsRFC6598(na *wire.NetAddress) bool {
|
||||
return rfc6598Net.Contains(na.IP)
|
||||
}
|
||||
|
||||
// IsValid returns whether or not the passed address is valid. The address is
|
||||
// considered invalid under the following circumstances:
|
||||
// IPv4: It is either a zero or all bits set address.
|
||||
// IPv6: It is either a zero or RFC3849 documentation address.
|
||||
func IsValid(na *wire.NetAddress) bool {
|
||||
// IsUnspecified returns if address is 0, so only all bits set, and
|
||||
// RFC3849 need to be explicitly checked.
|
||||
return na.IP != nil && !(na.IP.IsUnspecified() ||
|
||||
na.IP.Equal(net.IPv4bcast))
|
||||
}
|
||||
|
||||
// IsRoutable returns whether or not the passed address is routable over
|
||||
// the public internet. This is true as long as the address is valid and is not
|
||||
// in any reserved ranges.
|
||||
func IsRoutable(na *wire.NetAddress) bool {
|
||||
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
|
||||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
|
||||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
|
||||
IsLocal(na) || (IsRFC4193(na) && !IsOnionCatTor(na)))
|
||||
}
|
||||
|
||||
// GroupKey returns a string representing the network group an address is part
|
||||
// of. This is the /16 for IPv4, the /32 (/36 for he.net) for IPv6, the string
|
||||
// "local" for a local address, the string "tor:key" where key is the /4 of the
|
||||
// onion address for Tor address, and the string "unroutable" for an unroutable
|
||||
// address.
|
||||
func GroupKey(na *wire.NetAddress) string {
|
||||
if IsLocal(na) {
|
||||
return "local"
|
||||
}
|
||||
if !IsRoutable(na) {
|
||||
return "unroutable"
|
||||
}
|
||||
if IsIPv4(na) {
|
||||
return na.IP.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
if IsRFC6145(na) || IsRFC6052(na) {
|
||||
// last four bytes are the ip address
|
||||
ip := na.IP[12:16]
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
|
||||
if IsRFC3964(na) {
|
||||
ip := na.IP[2:6]
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
|
||||
}
|
||||
if IsRFC4380(na) {
|
||||
// teredo tunnels have the last 4 bytes as the v4 address XOR
|
||||
// 0xff.
|
||||
ip := net.IP(make([]byte, 4))
|
||||
for i, byte := range na.IP[12:16] {
|
||||
ip[i] = byte ^ 0xff
|
||||
}
|
||||
return ip.Mask(net.CIDRMask(16, 32)).String()
|
||||
}
|
||||
if IsOnionCatTor(na) {
|
||||
// group is keyed off the first 4 bits of the actual onion key.
|
||||
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
|
||||
}
|
||||
|
||||
// OK, so now we know ourselves to be a IPv6 address.
|
||||
// bitcoind uses /32 for everything, except for Hurricane Electric's
|
||||
// (he.net) IP range, which it uses /36 for.
|
||||
bits := 32
|
||||
if heNet.Contains(na.IP) {
|
||||
bits = 36
|
||||
}
|
||||
|
||||
return na.IP.Mask(net.CIDRMask(bits, 128)).String()
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
|
||||
github.com/conformal/btcd/addrmgr/network.go GroupKey 100.00% (23/23)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.reset 100.00% (6/6)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC5737 100.00% (4/4)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC1918 100.00% (4/4)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go New 100.00% (3/3)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go NetAddressKey 100.00% (2/2)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC4862 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.numAddresses 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/log.go init 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/log.go DisableLog 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go ipNet 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsIPv4 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsLocal 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsOnionCatTor 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC2544 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC3849 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC3927 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC3964 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC4193 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC4380 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC4843 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC6052 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC6145 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRFC6598 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsValid 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/network.go IsRoutable 100.00% (1/1)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.GetBestLocalAddress 94.74% (18/19)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddLocalAddress 90.91% (10/11)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go getReachabilityFrom 51.52% (17/33)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go ipString 50.00% (2/4)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.GetAddress 9.30% (4/43)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.deserializePeers 0.00% (0/50)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Good 0.00% (0/44)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.savePeers 0.00% (0/39)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.updateAddress 0.00% (0/30)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.expireNew 0.00% (0/22)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddressCache 0.00% (0/16)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.HostToNetAddress 0.00% (0/15)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.getNewBucket 0.00% (0/15)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddressByIP 0.00% (0/14)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.getTriedBucket 0.00% (0/14)
|
||||
github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.chance 0.00% (0/13)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.loadPeers 0.00% (0/11)
|
||||
github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.isBad 0.00% (0/11)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Connected 0.00% (0/10)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.addressHandler 0.00% (0/9)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.pickTried 0.00% (0/8)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.DeserializeNetAddress 0.00% (0/7)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Stop 0.00% (0/7)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Attempt 0.00% (0/7)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.Start 0.00% (0/6)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddresses 0.00% (0/4)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.NeedMoreAddresses 0.00% (0/3)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.NumAddresses 0.00% (0/3)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.AddAddress 0.00% (0/3)
|
||||
github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.LastAttempt 0.00% (0/1)
|
||||
github.com/conformal/btcd/addrmgr/knownaddress.go knownAddress.NetAddress 0.00% (0/1)
|
||||
github.com/conformal/btcd/addrmgr/addrmanager.go AddrManager.find 0.00% (0/1)
|
||||
github.com/conformal/btcd/addrmgr/log.go UseLogger 0.00% (0/1)
|
||||
github.com/conformal/btcd/addrmgr --------------------------------- 21.04% (113/537)
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
blockchain
|
||||
==========
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/blockchain)
|
||||
|
||||
Package blockchain implements bitcoin block handling and chain selection rules.
|
||||
The test coverage is currently only around 60%, but will be increasing over
|
||||
time. See `test_coverage.txt` for the gocov coverage report. Alternatively, if
|
||||
you are running a POSIX OS, you can run the `cov_report.sh` script for a
|
||||
real-time report. Package blockchain is licensed under the liberal ISC license.
|
||||
|
||||
There is an associated blog post about the release of this package
|
||||
[here](https://blog.conformal.com/btcchain-the-bitcoin-chain-package-from-bctd/).
|
||||
|
||||
This package has intentionally been designed so it can be used as a standalone
|
||||
package for any projects needing to handle processing of blocks into the bitcoin
|
||||
block chain.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/blockchain
|
||||
```
|
||||
|
||||
## Bitcoin Chain Processing Overview
|
||||
|
||||
Before a block is allowed into the block chain, it must go through an intensive
|
||||
series of validation rules. The following list serves as a general outline of
|
||||
those rules to provide some intuition into what is going on under the hood, but
|
||||
is by no means exhaustive:
|
||||
|
||||
- Reject duplicate blocks
|
||||
- Perform a series of sanity checks on the block and its transactions such as
|
||||
verifying proof of work, timestamps, number and character of transactions,
|
||||
transaction amounts, script complexity, and merkle root calculations
|
||||
- Compare the block against predetermined checkpoints for expected timestamps
|
||||
and difficulty based on elapsed time since the checkpoint
|
||||
- Save the most recent orphan blocks for a limited time in case their parent
|
||||
blocks become available
|
||||
- Stop processing if the block is an orphan as the rest of the processing
|
||||
depends on the block's position within the block chain
|
||||
- Perform a series of more thorough checks that depend on the block's position
|
||||
within the block chain such as verifying block difficulties adhere to
|
||||
difficulty retarget rules, timestamps are after the median of the last
|
||||
several blocks, all transactions are finalized, checkpoint blocks match, and
|
||||
block versions are in line with the previous blocks
|
||||
- Determine how the block fits into the chain and perform different actions
|
||||
accordingly in order to ensure any side chains which have higher difficulty
|
||||
than the main chain become the new main chain
|
||||
- When a block is being connected to the main chain (either through
|
||||
reorganization of a side chain to the main chain or just extending the
|
||||
main chain), perform further checks on the block's transactions such as
|
||||
verifying transaction duplicates, script complexity for the combination of
|
||||
connected scripts, coinbase maturity, double spends, and connected
|
||||
transaction values
|
||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||
coins
|
||||
- Insert the block into the block database
|
||||
|
||||
## Examples
|
||||
|
||||
* [ProcessBlock Example](http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BlockChain-ProcessBlock)
|
||||
Demonstrates how to create a new chain instance and use ProcessBlock to
|
||||
attempt to add a block to the chain. This example intentionally
|
||||
attempts to insert a duplicate genesis block to illustrate how an invalid
|
||||
block is handled.
|
||||
|
||||
* [CompactToBig Example](http://godoc.org/github.com/btcsuite/btcd/blockchain#example-CompactToBig)
|
||||
Demonstrates how to convert the compact "bits" in a block header which
|
||||
represent the target difficulty to a big integer and display it using the
|
||||
typical hex notation.
|
||||
|
||||
* [BigToCompact Example](http://godoc.org/github.com/btcsuite/btcd/blockchain#example-BigToCompact)
|
||||
Demonstrates how to convert a target difficulty into the
|
||||
compact "bits" in a block header which represent that target difficulty.
|
||||
|
||||
## GPG Verification Key
|
||||
|
||||
All official release tags are signed by Conformal so users can ensure the code
|
||||
has not been tampered with and is coming from the btcsuite developers. To
|
||||
verify the signature perform the following:
|
||||
|
||||
- Download the public key from the Conformal website at
|
||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
||||
|
||||
- Import the public key into your GPG keyring:
|
||||
```bash
|
||||
gpg --import GIT-GPG-KEY-conformal.txt
|
||||
```
|
||||
|
||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
||||
placeholder for the specific tag:
|
||||
```bash
|
||||
git tag -v TAG_NAME
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Package blockchain is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// maybeAcceptBlock potentially accepts a block into the block chain and, if
|
||||
// accepted, returns whether or not it is on the main chain. It performs
|
||||
// several validation checks which depend on its position within the block chain
|
||||
// before adding it. The block is expected to have already gone through
|
||||
// ProcessBlock before calling this function with it.
|
||||
//
|
||||
// The flags are also passed to checkBlockContext and connectBestChain. See
|
||||
// their documentation for how the flags modify their behavior.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||
// The height of this block is one more than the referenced previous
|
||||
// block.
|
||||
prevHash := &block.MsgBlock().Header.PrevBlock
|
||||
prevNode := b.index.LookupNode(prevHash)
|
||||
if prevNode == nil {
|
||||
str := fmt.Sprintf("previous block %s is unknown", prevHash)
|
||||
return false, ruleError(ErrPreviousBlockUnknown, str)
|
||||
} else if b.index.NodeStatus(prevNode).KnownInvalid() {
|
||||
str := fmt.Sprintf("previous block %s is known to be invalid", prevHash)
|
||||
return false, ruleError(ErrInvalidAncestorBlock, str)
|
||||
}
|
||||
|
||||
blockHeight := prevNode.height + 1
|
||||
block.SetHeight(blockHeight)
|
||||
|
||||
// The block must pass all of the validation rules which depend on the
|
||||
// position of the block within the block chain.
|
||||
err := b.checkBlockContext(block, prevNode, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Insert the block into the database if it's not already there. Even
|
||||
// though it is possible the block will ultimately fail to connect, it
|
||||
// has already passed all proof-of-work and validity tests which means
|
||||
// it would be prohibitively expensive for an attacker to fill up the
|
||||
// disk with a bunch of blocks that fail to connect. This is necessary
|
||||
// since it allows block download to be decoupled from the much more
|
||||
// expensive connection logic. It also has some other nice properties
|
||||
// such as making blocks that never become part of the main chain or
|
||||
// blocks that fail to connect available for further analysis.
|
||||
err = b.db.Update(func(dbTx database.Tx) error {
|
||||
return dbStoreBlock(dbTx, block)
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Create a new block node for the block and add it to the node index. Even
|
||||
// if the block ultimately gets connected to the main chain, it starts out
|
||||
// on a side chain.
|
||||
blockHeader := &block.MsgBlock().Header
|
||||
newNode := newBlockNode(blockHeader, prevNode)
|
||||
newNode.status = statusDataStored
|
||||
|
||||
b.index.AddNode(newNode)
|
||||
err = b.index.flushToDB()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Connect the passed block to the chain while respecting proper chain
|
||||
// selection according to the chain with the most proof of work. This
|
||||
// also handles validation of the transaction scripts.
|
||||
isMainChain, err := b.connectBestChain(newNode, block, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Notify the caller that the new block was accepted into the block
|
||||
// chain. The caller would typically want to react by relaying the
|
||||
// inventory to other peers.
|
||||
b.chainLock.Unlock()
|
||||
b.sendNotification(NTBlockAccepted, block)
|
||||
b.chainLock.Lock()
|
||||
|
||||
return isMainChain, nil
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
// Copyright (c) 2015-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// blockStatus is a bit field representing the validation state of the block.
|
||||
type blockStatus byte
|
||||
|
||||
const (
|
||||
// statusDataStored indicates that the block's payload is stored on disk.
|
||||
statusDataStored blockStatus = 1 << iota
|
||||
|
||||
// statusValid indicates that the block has been fully validated.
|
||||
statusValid
|
||||
|
||||
// statusValidateFailed indicates that the block has failed validation.
|
||||
statusValidateFailed
|
||||
|
||||
// statusInvalidAncestor indicates that one of the block's ancestors has
|
||||
// has failed validation, thus the block is also invalid.
|
||||
statusInvalidAncestor
|
||||
|
||||
// statusNone indicates that the block has no validation state flags set.
|
||||
//
|
||||
// NOTE: This must be defined last in order to avoid influencing iota.
|
||||
statusNone blockStatus = 0
|
||||
)
|
||||
|
||||
// HaveData returns whether the full block data is stored in the database. This
|
||||
// will return false for a block node where only the header is downloaded or
|
||||
// kept.
|
||||
func (status blockStatus) HaveData() bool {
|
||||
return status&statusDataStored != 0
|
||||
}
|
||||
|
||||
// KnownValid returns whether the block is known to be valid. This will return
|
||||
// false for a valid block that has not been fully validated yet.
|
||||
func (status blockStatus) KnownValid() bool {
|
||||
return status&statusValid != 0
|
||||
}
|
||||
|
||||
// KnownInvalid returns whether the block is known to be invalid. This may be
|
||||
// because the block itself failed validation or any of its ancestors is
|
||||
// invalid. This will return false for invalid blocks that have not been proven
|
||||
// invalid yet.
|
||||
func (status blockStatus) KnownInvalid() bool {
|
||||
return status&(statusValidateFailed|statusInvalidAncestor) != 0
|
||||
}
|
||||
|
||||
// blockNode represents a block within the block chain and is primarily used to
|
||||
// aid in selecting the best chain to be the main chain. The main chain is
|
||||
// stored into the block database.
|
||||
type blockNode struct {
|
||||
// NOTE: Additions, deletions, or modifications to the order of the
|
||||
// definitions in this struct should not be changed without considering
|
||||
// how it affects alignment on 64-bit platforms. The current order is
|
||||
// specifically crafted to result in minimal padding. There will be
|
||||
// hundreds of thousands of these in memory, so a few extra bytes of
|
||||
// padding adds up.
|
||||
|
||||
// parent is the parent block for this node.
|
||||
parent *blockNode
|
||||
|
||||
// hash is the double sha 256 of the block.
|
||||
hash chainhash.Hash
|
||||
|
||||
// workSum is the total amount of work in the chain up to and including
|
||||
// this node.
|
||||
workSum *big.Int
|
||||
|
||||
// height is the position in the block chain.
|
||||
height int32
|
||||
|
||||
// Some fields from block headers to aid in best chain selection and
|
||||
// reconstructing headers from memory. These must be treated as
|
||||
// immutable and are intentionally ordered to avoid padding on 64-bit
|
||||
// platforms.
|
||||
version int32
|
||||
bits uint32
|
||||
nonce uint32
|
||||
timestamp int64
|
||||
merkleRoot chainhash.Hash
|
||||
|
||||
// status is a bitfield representing the validation state of the block. The
|
||||
// status field, unlike the other fields, may be written to and so should
|
||||
// only be accessed using the concurrent-safe NodeStatus method on
|
||||
// blockIndex once the node has been added to the global index.
|
||||
status blockStatus
|
||||
}
|
||||
|
||||
// initBlockNode initializes a block node from the given header and parent node,
|
||||
// calculating the height and workSum from the respective fields on the parent.
|
||||
// This function is NOT safe for concurrent access. It must only be called when
|
||||
// initially creating a node.
|
||||
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parent *blockNode) {
|
||||
*node = blockNode{
|
||||
hash: blockHeader.BlockHash(),
|
||||
workSum: CalcWork(blockHeader.Bits),
|
||||
version: blockHeader.Version,
|
||||
bits: blockHeader.Bits,
|
||||
nonce: blockHeader.Nonce,
|
||||
timestamp: blockHeader.Timestamp.Unix(),
|
||||
merkleRoot: blockHeader.MerkleRoot,
|
||||
}
|
||||
if parent != nil {
|
||||
node.parent = parent
|
||||
node.height = parent.height + 1
|
||||
node.workSum = node.workSum.Add(parent.workSum, node.workSum)
|
||||
}
|
||||
}
|
||||
|
||||
// newBlockNode returns a new block node for the given block header and parent
|
||||
// node, calculating the height and workSum from the respective fields on the
|
||||
// parent. This function is NOT safe for concurrent access.
|
||||
func newBlockNode(blockHeader *wire.BlockHeader, parent *blockNode) *blockNode {
|
||||
var node blockNode
|
||||
initBlockNode(&node, blockHeader, parent)
|
||||
return &node
|
||||
}
|
||||
|
||||
// Header constructs a block header from the node and returns it.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) Header() wire.BlockHeader {
|
||||
// No lock is needed because all accessed fields are immutable.
|
||||
prevHash := &zeroHash
|
||||
if node.parent != nil {
|
||||
prevHash = &node.parent.hash
|
||||
}
|
||||
return wire.BlockHeader{
|
||||
Version: node.version,
|
||||
PrevBlock: *prevHash,
|
||||
MerkleRoot: node.merkleRoot,
|
||||
Timestamp: time.Unix(node.timestamp, 0),
|
||||
Bits: node.bits,
|
||||
Nonce: node.nonce,
|
||||
}
|
||||
}
|
||||
|
||||
// Ancestor returns the ancestor block node at the provided height by following
|
||||
// the chain backwards from this node. The returned block will be nil when a
|
||||
// height is requested that is after the height of the passed node or is less
|
||||
// than zero.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) Ancestor(height int32) *blockNode {
|
||||
if height < 0 || height > node.height {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := node
|
||||
for ; n != nil && n.height != height; n = n.parent {
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// RelativeAncestor returns the ancestor block node a relative 'distance' blocks
|
||||
// before this node. This is equivalent to calling Ancestor with the node's
|
||||
// height minus provided distance.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) RelativeAncestor(distance int32) *blockNode {
|
||||
return node.Ancestor(node.height - distance)
|
||||
}
|
||||
|
||||
// CalcPastMedianTime calculates the median time of the previous few blocks
|
||||
// prior to, and including, the block node.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (node *blockNode) CalcPastMedianTime() time.Time {
|
||||
// Create a slice of the previous few block timestamps used to calculate
|
||||
// the median per the number defined by the constant medianTimeBlocks.
|
||||
timestamps := make([]int64, medianTimeBlocks)
|
||||
numNodes := 0
|
||||
iterNode := node
|
||||
for i := 0; i < medianTimeBlocks && iterNode != nil; i++ {
|
||||
timestamps[i] = iterNode.timestamp
|
||||
numNodes++
|
||||
|
||||
iterNode = iterNode.parent
|
||||
}
|
||||
|
||||
// Prune the slice to the actual number of available timestamps which
|
||||
// will be fewer than desired near the beginning of the block chain
|
||||
// and sort them.
|
||||
timestamps = timestamps[:numNodes]
|
||||
sort.Sort(timeSorter(timestamps))
|
||||
|
||||
// NOTE: The consensus rules incorrectly calculate the median for even
|
||||
// numbers of blocks. A true median averages the middle two elements
|
||||
// for a set with an even number of elements in it. Since the constant
|
||||
// for the previous number of blocks to be used is odd, this is only an
|
||||
// issue for a few blocks near the beginning of the chain. I suspect
|
||||
// this is an optimization even though the result is slightly wrong for
|
||||
// a few of the first blocks since after the first few blocks, there
|
||||
// will always be an odd number of blocks in the set per the constant.
|
||||
//
|
||||
// This code follows suit to ensure the same rules are used, however, be
|
||||
// aware that should the medianTimeBlocks constant ever be changed to an
|
||||
// even number, this code will be wrong.
|
||||
medianTimestamp := timestamps[numNodes/2]
|
||||
return time.Unix(medianTimestamp, 0)
|
||||
}
|
||||
|
||||
// blockIndex provides facilities for keeping track of an in-memory index of the
|
||||
// block chain. Although the name block chain suggests a single chain of
|
||||
// blocks, it is actually a tree-shaped structure where any node can have
|
||||
// multiple children. However, there can only be one active branch which does
|
||||
// indeed form a chain from the tip all the way back to the genesis block.
|
||||
type blockIndex struct {
|
||||
// The following fields are set when the instance is created and can't
|
||||
// be changed afterwards, so there is no need to protect them with a
|
||||
// separate mutex.
|
||||
db database.DB
|
||||
chainParams *chaincfg.Params
|
||||
|
||||
sync.RWMutex
|
||||
index map[chainhash.Hash]*blockNode
|
||||
dirty map[*blockNode]struct{}
|
||||
}
|
||||
|
||||
// newBlockIndex returns a new empty instance of a block index. The index will
|
||||
// be dynamically populated as block nodes are loaded from the database and
|
||||
// manually added.
|
||||
func newBlockIndex(db database.DB, chainParams *chaincfg.Params) *blockIndex {
|
||||
return &blockIndex{
|
||||
db: db,
|
||||
chainParams: chainParams,
|
||||
index: make(map[chainhash.Hash]*blockNode),
|
||||
dirty: make(map[*blockNode]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// HaveBlock returns whether or not the block index contains the provided hash.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) HaveBlock(hash *chainhash.Hash) bool {
|
||||
bi.RLock()
|
||||
_, hasBlock := bi.index[*hash]
|
||||
bi.RUnlock()
|
||||
return hasBlock
|
||||
}
|
||||
|
||||
// LookupNode returns the block node identified by the provided hash. It will
|
||||
// return nil if there is no entry for the hash.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) LookupNode(hash *chainhash.Hash) *blockNode {
|
||||
bi.RLock()
|
||||
node := bi.index[*hash]
|
||||
bi.RUnlock()
|
||||
return node
|
||||
}
|
||||
|
||||
// AddNode adds the provided node to the block index and marks it as dirty.
|
||||
// Duplicate entries are not checked so it is up to caller to avoid adding them.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) AddNode(node *blockNode) {
|
||||
bi.Lock()
|
||||
bi.addNode(node)
|
||||
bi.dirty[node] = struct{}{}
|
||||
bi.Unlock()
|
||||
}
|
||||
|
||||
// addNode adds the provided node to the block index, but does not mark it as
|
||||
// dirty. This can be used while initializing the block index.
|
||||
//
|
||||
// This function is NOT safe for concurrent access.
|
||||
func (bi *blockIndex) addNode(node *blockNode) {
|
||||
bi.index[node.hash] = node
|
||||
}
|
||||
|
||||
// NodeStatus provides concurrent-safe access to the status field of a node.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) NodeStatus(node *blockNode) blockStatus {
|
||||
bi.RLock()
|
||||
status := node.status
|
||||
bi.RUnlock()
|
||||
return status
|
||||
}
|
||||
|
||||
// SetStatusFlags flips the provided status flags on the block node to on,
|
||||
// regardless of whether they were on or off previously. This does not unset any
|
||||
// flags currently on.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) SetStatusFlags(node *blockNode, flags blockStatus) {
|
||||
bi.Lock()
|
||||
node.status |= flags
|
||||
bi.dirty[node] = struct{}{}
|
||||
bi.Unlock()
|
||||
}
|
||||
|
||||
// UnsetStatusFlags flips the provided status flags on the block node to off,
|
||||
// regardless of whether they were on or off previously.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (bi *blockIndex) UnsetStatusFlags(node *blockNode, flags blockStatus) {
|
||||
bi.Lock()
|
||||
node.status &^= flags
|
||||
bi.dirty[node] = struct{}{}
|
||||
bi.Unlock()
|
||||
}
|
||||
|
||||
// flushToDB writes all dirty block nodes to the database. If all writes
|
||||
// succeed, this clears the dirty set.
|
||||
func (bi *blockIndex) flushToDB() error {
|
||||
bi.Lock()
|
||||
if len(bi.dirty) == 0 {
|
||||
bi.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
err := bi.db.Update(func(dbTx database.Tx) error {
|
||||
for node := range bi.dirty {
|
||||
err := dbStoreBlockNode(dbTx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// If write was successful, clear the dirty set.
|
||||
if err == nil {
|
||||
bi.dirty = make(map[*blockNode]struct{})
|
||||
}
|
||||
|
||||
bi.Unlock()
|
||||
return err
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,423 @@
|
|||
// Copyright (c) 2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// approxNodesPerWeek is an approximation of the number of new blocks there are
|
||||
// in a week on average.
|
||||
const approxNodesPerWeek = 6 * 24 * 7
|
||||
|
||||
// log2FloorMasks defines the masks to use when quickly calculating
|
||||
// floor(log2(x)) in a constant log2(32) = 5 steps, where x is a uint32, using
|
||||
// shifts. They are derived from (2^(2^x) - 1) * (2^(2^x)), for x in 4..0.
|
||||
var log2FloorMasks = []uint32{0xffff0000, 0xff00, 0xf0, 0xc, 0x2}
|
||||
|
||||
// fastLog2Floor calculates and returns floor(log2(x)) in a constant 5 steps.
|
||||
func fastLog2Floor(n uint32) uint8 {
|
||||
rv := uint8(0)
|
||||
exponent := uint8(16)
|
||||
for i := 0; i < 5; i++ {
|
||||
if n&log2FloorMasks[i] != 0 {
|
||||
rv += exponent
|
||||
n >>= exponent
|
||||
}
|
||||
exponent >>= 1
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
// chainView provides a flat view of a specific branch of the block chain from
|
||||
// its tip back to the genesis block and provides various convenience functions
|
||||
// for comparing chains.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
//
|
||||
// The chain view for the branch ending in 6a consists of:
|
||||
// genesis -> 1 -> 2 -> 3 -> 4a -> 5a -> 6a
|
||||
type chainView struct {
|
||||
mtx sync.Mutex
|
||||
nodes []*blockNode
|
||||
}
|
||||
|
||||
// newChainView returns a new chain view for the given tip block node. Passing
|
||||
// nil as the tip will result in a chain view that is not initialized. The tip
|
||||
// can be updated at any time via the setTip function.
|
||||
func newChainView(tip *blockNode) *chainView {
|
||||
// The mutex is intentionally not held since this is a constructor.
|
||||
var c chainView
|
||||
c.setTip(tip)
|
||||
return &c
|
||||
}
|
||||
|
||||
// genesis returns the genesis block for the chain view. This only differs from
|
||||
// the exported version in that it is up to the caller to ensure the lock is
|
||||
// held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) genesis() *blockNode {
|
||||
if len(c.nodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.nodes[0]
|
||||
}
|
||||
|
||||
// Genesis returns the genesis block for the chain view.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Genesis() *blockNode {
|
||||
c.mtx.Lock()
|
||||
genesis := c.genesis()
|
||||
c.mtx.Unlock()
|
||||
return genesis
|
||||
}
|
||||
|
||||
// tip returns the current tip block node for the chain view. It will return
|
||||
// nil if there is no tip. This only differs from the exported version in that
|
||||
// it is up to the caller to ensure the lock is held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) tip() *blockNode {
|
||||
if len(c.nodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.nodes[len(c.nodes)-1]
|
||||
}
|
||||
|
||||
// Tip returns the current tip block node for the chain view. It will return
|
||||
// nil if there is no tip.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Tip() *blockNode {
|
||||
c.mtx.Lock()
|
||||
tip := c.tip()
|
||||
c.mtx.Unlock()
|
||||
return tip
|
||||
}
|
||||
|
||||
// setTip sets the chain view to use the provided block node as the current tip
|
||||
// and ensures the view is consistent by populating it with the nodes obtained
|
||||
// by walking backwards all the way to genesis block as necessary. Further
|
||||
// calls will only perform the minimum work needed, so switching between chain
|
||||
// tips is efficient. This only differs from the exported version in that it is
|
||||
// up to the caller to ensure the lock is held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for writes).
|
||||
func (c *chainView) setTip(node *blockNode) {
|
||||
if node == nil {
|
||||
// Keep the backing array around for potential future use.
|
||||
c.nodes = c.nodes[:0]
|
||||
return
|
||||
}
|
||||
|
||||
// Create or resize the slice that will hold the block nodes to the
|
||||
// provided tip height. When creating the slice, it is created with
|
||||
// some additional capacity for the underlying array as append would do
|
||||
// in order to reduce overhead when extending the chain later. As long
|
||||
// as the underlying array already has enough capacity, simply expand or
|
||||
// contract the slice accordingly. The additional capacity is chosen
|
||||
// such that the array should only have to be extended about once a
|
||||
// week.
|
||||
needed := node.height + 1
|
||||
if int32(cap(c.nodes)) < needed {
|
||||
nodes := make([]*blockNode, needed, needed+approxNodesPerWeek)
|
||||
copy(nodes, c.nodes)
|
||||
c.nodes = nodes
|
||||
} else {
|
||||
prevLen := int32(len(c.nodes))
|
||||
c.nodes = c.nodes[0:needed]
|
||||
for i := prevLen; i < needed; i++ {
|
||||
c.nodes[i] = nil
|
||||
}
|
||||
}
|
||||
|
||||
for node != nil && c.nodes[node.height] != node {
|
||||
c.nodes[node.height] = node
|
||||
node = node.parent
|
||||
}
|
||||
}
|
||||
|
||||
// SetTip sets the chain view to use the provided block node as the current tip
|
||||
// and ensures the view is consistent by populating it with the nodes obtained
|
||||
// by walking backwards all the way to genesis block as necessary. Further
|
||||
// calls will only perform the minimum work needed, so switching between chain
|
||||
// tips is efficient.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) SetTip(node *blockNode) {
|
||||
c.mtx.Lock()
|
||||
c.setTip(node)
|
||||
c.mtx.Unlock()
|
||||
}
|
||||
|
||||
// height returns the height of the tip of the chain view. It will return -1 if
|
||||
// there is no tip (which only happens if the chain view has not been
|
||||
// initialized). This only differs from the exported version in that it is up
|
||||
// to the caller to ensure the lock is held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) height() int32 {
|
||||
return int32(len(c.nodes) - 1)
|
||||
}
|
||||
|
||||
// Height returns the height of the tip of the chain view. It will return -1 if
|
||||
// there is no tip (which only happens if the chain view has not been
|
||||
// initialized).
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Height() int32 {
|
||||
c.mtx.Lock()
|
||||
height := c.height()
|
||||
c.mtx.Unlock()
|
||||
return height
|
||||
}
|
||||
|
||||
// nodeByHeight returns the block node at the specified height. Nil will be
|
||||
// returned if the height does not exist. This only differs from the exported
|
||||
// version in that it is up to the caller to ensure the lock is held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) nodeByHeight(height int32) *blockNode {
|
||||
if height < 0 || height >= int32(len(c.nodes)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.nodes[height]
|
||||
}
|
||||
|
||||
// NodeByHeight returns the block node at the specified height. Nil will be
|
||||
// returned if the height does not exist.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) NodeByHeight(height int32) *blockNode {
|
||||
c.mtx.Lock()
|
||||
node := c.nodeByHeight(height)
|
||||
c.mtx.Unlock()
|
||||
return node
|
||||
}
|
||||
|
||||
// Equals returns whether or not two chain views are the same. Uninitialized
|
||||
// views (tip set to nil) are considered equal.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Equals(other *chainView) bool {
|
||||
c.mtx.Lock()
|
||||
other.mtx.Lock()
|
||||
equals := len(c.nodes) == len(other.nodes) && c.tip() == other.tip()
|
||||
other.mtx.Unlock()
|
||||
c.mtx.Unlock()
|
||||
return equals
|
||||
}
|
||||
|
||||
// contains returns whether or not the chain view contains the passed block
|
||||
// node. This only differs from the exported version in that it is up to the
|
||||
// caller to ensure the lock is held.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) contains(node *blockNode) bool {
|
||||
return c.nodeByHeight(node.height) == node
|
||||
}
|
||||
|
||||
// Contains returns whether or not the chain view contains the passed block
|
||||
// node.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Contains(node *blockNode) bool {
|
||||
c.mtx.Lock()
|
||||
contains := c.contains(node)
|
||||
c.mtx.Unlock()
|
||||
return contains
|
||||
}
|
||||
|
||||
// next returns the successor to the provided node for the chain view. It will
|
||||
// return nil if there is no successor or the provided node is not part of the
|
||||
// view. This only differs from the exported version in that it is up to the
|
||||
// caller to ensure the lock is held.
|
||||
//
|
||||
// See the comment on the exported function for more details.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) next(node *blockNode) *blockNode {
|
||||
if node == nil || !c.contains(node) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.nodeByHeight(node.height + 1)
|
||||
}
|
||||
|
||||
// Next returns the successor to the provided node for the chain view. It will
|
||||
// return nil if there is no successfor or the provided node is not part of the
|
||||
// view.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 4a -> 5a -> 6a
|
||||
//
|
||||
// Further, assume the view is for the longer chain depicted above. That is to
|
||||
// say it consists of:
|
||||
// genesis -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
|
||||
//
|
||||
// Invoking this function with block node 5 would return block node 6 while
|
||||
// invoking it with block node 5a would return nil since that node is not part
|
||||
// of the view.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Next(node *blockNode) *blockNode {
|
||||
c.mtx.Lock()
|
||||
next := c.next(node)
|
||||
c.mtx.Unlock()
|
||||
return next
|
||||
}
|
||||
|
||||
// findFork returns the final common block between the provided node and the
|
||||
// the chain view. It will return nil if there is no common block. This only
|
||||
// differs from the exported version in that it is up to the caller to ensure
|
||||
// the lock is held.
|
||||
//
|
||||
// See the exported FindFork comments for more details.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) findFork(node *blockNode) *blockNode {
|
||||
// No fork point for node that doesn't exist.
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// When the height of the passed node is higher than the height of the
|
||||
// tip of the current chain view, walk backwards through the nodes of
|
||||
// the other chain until the heights match (or there or no more nodes in
|
||||
// which case there is no common node between the two).
|
||||
//
|
||||
// NOTE: This isn't strictly necessary as the following section will
|
||||
// find the node as well, however, it is more efficient to avoid the
|
||||
// contains check since it is already known that the common node can't
|
||||
// possibly be past the end of the current chain view. It also allows
|
||||
// this code to take advantage of any potential future optimizations to
|
||||
// the Ancestor function such as using an O(log n) skip list.
|
||||
chainHeight := c.height()
|
||||
if node.height > chainHeight {
|
||||
node = node.Ancestor(chainHeight)
|
||||
}
|
||||
|
||||
// Walk the other chain backwards as long as the current one does not
|
||||
// contain the node or there are no more nodes in which case there is no
|
||||
// common node between the two.
|
||||
for node != nil && !c.contains(node) {
|
||||
node = node.parent
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
// FindFork returns the final common block between the provided node and the
|
||||
// the chain view. It will return nil if there is no common block.
|
||||
//
|
||||
// For example, assume a block chain with a side chain as depicted below:
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8
|
||||
// \-> 6a -> 7a
|
||||
//
|
||||
// Further, assume the view is for the longer chain depicted above. That is to
|
||||
// say it consists of:
|
||||
// genesis -> 1 -> 2 -> ... -> 5 -> 6 -> 7 -> 8.
|
||||
//
|
||||
// Invoking this function with block node 7a would return block node 5 while
|
||||
// invoking it with block node 7 would return itself since it is already part of
|
||||
// the branch formed by the view.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) FindFork(node *blockNode) *blockNode {
|
||||
c.mtx.Lock()
|
||||
fork := c.findFork(node)
|
||||
c.mtx.Unlock()
|
||||
return fork
|
||||
}
|
||||
|
||||
// blockLocator returns a block locator for the passed block node. The passed
|
||||
// node can be nil in which case the block locator for the current tip
|
||||
// associated with the view will be returned. This only differs from the
|
||||
// exported version in that it is up to the caller to ensure the lock is held.
|
||||
//
|
||||
// See the exported BlockLocator function comments for more details.
|
||||
//
|
||||
// This function MUST be called with the view mutex locked (for reads).
|
||||
func (c *chainView) blockLocator(node *blockNode) BlockLocator {
|
||||
// Use the current tip if requested.
|
||||
if node == nil {
|
||||
node = c.tip()
|
||||
}
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Calculate the max number of entries that will ultimately be in the
|
||||
// block locator. See the description of the algorithm for how these
|
||||
// numbers are derived.
|
||||
var maxEntries uint8
|
||||
if node.height <= 12 {
|
||||
maxEntries = uint8(node.height) + 1
|
||||
} else {
|
||||
// Requested hash itself + previous 10 entries + genesis block.
|
||||
// Then floor(log2(height-10)) entries for the skip portion.
|
||||
adjustedHeight := uint32(node.height) - 10
|
||||
maxEntries = 12 + fastLog2Floor(adjustedHeight)
|
||||
}
|
||||
locator := make(BlockLocator, 0, maxEntries)
|
||||
|
||||
step := int32(1)
|
||||
for node != nil {
|
||||
locator = append(locator, &node.hash)
|
||||
|
||||
// Nothing more to add once the genesis block has been added.
|
||||
if node.height == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Calculate height of previous node to include ensuring the
|
||||
// final node is the genesis block.
|
||||
height := node.height - step
|
||||
if height < 0 {
|
||||
height = 0
|
||||
}
|
||||
|
||||
// When the node is in the current chain view, all of its
|
||||
// ancestors must be too, so use a much faster O(1) lookup in
|
||||
// that case. Otherwise, fall back to walking backwards through
|
||||
// the nodes of the other chain to the correct ancestor.
|
||||
if c.contains(node) {
|
||||
node = c.nodes[height]
|
||||
} else {
|
||||
node = node.Ancestor(height)
|
||||
}
|
||||
|
||||
// Once 11 entries have been included, start doubling the
|
||||
// distance between included hashes.
|
||||
if len(locator) > 10 {
|
||||
step *= 2
|
||||
}
|
||||
}
|
||||
|
||||
return locator
|
||||
}
|
||||
|
||||
// BlockLocator returns a block locator for the passed block node. The passed
|
||||
// node can be nil in which case the block locator for the current tip
|
||||
// associated with the view will be returned.
|
||||
//
|
||||
// See the BlockLocator type for details on the algorithm used to create a block
|
||||
// locator.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) BlockLocator(node *blockNode) BlockLocator {
|
||||
c.mtx.Lock()
|
||||
locator := c.blockLocator(node)
|
||||
c.mtx.Unlock()
|
||||
return locator
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// CheckpointConfirmations is the number of blocks before the end of the current
|
||||
// best block chain that a good checkpoint candidate must be.
|
||||
const CheckpointConfirmations = 2016
|
||||
|
||||
// newHashFromStr converts the passed big-endian hex string into a
|
||||
// chainhash.Hash. It only differs from the one available in chainhash in that
|
||||
// it ignores the error since it will only (and must only) be called with
|
||||
// hard-coded, and therefore known good, hashes.
|
||||
func newHashFromStr(hexStr string) *chainhash.Hash {
|
||||
hash, _ := chainhash.NewHashFromStr(hexStr)
|
||||
return hash
|
||||
}
|
||||
|
||||
// Checkpoints returns a slice of checkpoints (regardless of whether they are
|
||||
// already known). When there are no checkpoints for the chain, it will return
|
||||
// nil.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) Checkpoints() []chaincfg.Checkpoint {
|
||||
return b.checkpoints
|
||||
}
|
||||
|
||||
// HasCheckpoints returns whether this BlockChain has checkpoints defined.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) HasCheckpoints() bool {
|
||||
return len(b.checkpoints) > 0
|
||||
}
|
||||
|
||||
// LatestCheckpoint returns the most recent checkpoint (regardless of whether it
|
||||
// is already known). When there are no defined checkpoints for the active chain
|
||||
// instance, it will return nil.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) LatestCheckpoint() *chaincfg.Checkpoint {
|
||||
if !b.HasCheckpoints() {
|
||||
return nil
|
||||
}
|
||||
return &b.checkpoints[len(b.checkpoints)-1]
|
||||
}
|
||||
|
||||
// verifyCheckpoint returns whether the passed block height and hash combination
|
||||
// match the checkpoint data. It also returns true if there is no checkpoint
|
||||
// data for the passed block height.
|
||||
func (b *BlockChain) verifyCheckpoint(height int32, hash *chainhash.Hash) bool {
|
||||
if !b.HasCheckpoints() {
|
||||
return true
|
||||
}
|
||||
|
||||
// Nothing to check if there is no checkpoint data for the block height.
|
||||
checkpoint, exists := b.checkpointsByHeight[height]
|
||||
if !exists {
|
||||
return true
|
||||
}
|
||||
|
||||
if !checkpoint.Hash.IsEqual(hash) {
|
||||
return false
|
||||
}
|
||||
|
||||
log.Infof("Verified checkpoint at height %d/block %s", checkpoint.Height,
|
||||
checkpoint.Hash)
|
||||
return true
|
||||
}
|
||||
|
||||
// findPreviousCheckpoint finds the most recent checkpoint that is already
|
||||
// available in the downloaded portion of the block chain and returns the
|
||||
// associated block node. It returns nil if a checkpoint can't be found (this
|
||||
// should really only happen for blocks before the first checkpoint).
|
||||
//
|
||||
// This function MUST be called with the chain lock held (for reads).
|
||||
func (b *BlockChain) findPreviousCheckpoint() (*blockNode, error) {
|
||||
if !b.HasCheckpoints() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Perform the initial search to find and cache the latest known
|
||||
// checkpoint if the best chain is not known yet or we haven't already
|
||||
// previously searched.
|
||||
checkpoints := b.checkpoints
|
||||
numCheckpoints := len(checkpoints)
|
||||
if b.checkpointNode == nil && b.nextCheckpoint == nil {
|
||||
// Loop backwards through the available checkpoints to find one
|
||||
// that is already available.
|
||||
for i := numCheckpoints - 1; i >= 0; i-- {
|
||||
node := b.index.LookupNode(checkpoints[i].Hash)
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Checkpoint found. Cache it for future lookups and
|
||||
// set the next expected checkpoint accordingly.
|
||||
b.checkpointNode = node
|
||||
if i < numCheckpoints-1 {
|
||||
b.nextCheckpoint = &checkpoints[i+1]
|
||||
}
|
||||
return b.checkpointNode, nil
|
||||
}
|
||||
|
||||
// No known latest checkpoint. This will only happen on blocks
|
||||
// before the first known checkpoint. So, set the next expected
|
||||
// checkpoint to the first checkpoint and return the fact there
|
||||
// is no latest known checkpoint block.
|
||||
b.nextCheckpoint = &checkpoints[0]
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// At this point we've already searched for the latest known checkpoint,
|
||||
// so when there is no next checkpoint, the current checkpoint lockin
|
||||
// will always be the latest known checkpoint.
|
||||
if b.nextCheckpoint == nil {
|
||||
return b.checkpointNode, nil
|
||||
}
|
||||
|
||||
// When there is a next checkpoint and the height of the current best
|
||||
// chain does not exceed it, the current checkpoint lockin is still
|
||||
// the latest known checkpoint.
|
||||
if b.bestChain.Tip().height < b.nextCheckpoint.Height {
|
||||
return b.checkpointNode, nil
|
||||
}
|
||||
|
||||
// We've reached or exceeded the next checkpoint height. Note that
|
||||
// once a checkpoint lockin has been reached, forks are prevented from
|
||||
// any blocks before the checkpoint, so we don't have to worry about the
|
||||
// checkpoint going away out from under us due to a chain reorganize.
|
||||
|
||||
// Cache the latest known checkpoint for future lookups. Note that if
|
||||
// this lookup fails something is very wrong since the chain has already
|
||||
// passed the checkpoint which was verified as accurate before inserting
|
||||
// it.
|
||||
checkpointNode := b.index.LookupNode(b.nextCheckpoint.Hash)
|
||||
if checkpointNode == nil {
|
||||
return nil, AssertError(fmt.Sprintf("findPreviousCheckpoint "+
|
||||
"failed lookup of known good block node %s",
|
||||
b.nextCheckpoint.Hash))
|
||||
}
|
||||
b.checkpointNode = checkpointNode
|
||||
|
||||
// Set the next expected checkpoint.
|
||||
checkpointIndex := -1
|
||||
for i := numCheckpoints - 1; i >= 0; i-- {
|
||||
if checkpoints[i].Hash.IsEqual(b.nextCheckpoint.Hash) {
|
||||
checkpointIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
b.nextCheckpoint = nil
|
||||
if checkpointIndex != -1 && checkpointIndex < numCheckpoints-1 {
|
||||
b.nextCheckpoint = &checkpoints[checkpointIndex+1]
|
||||
}
|
||||
|
||||
return b.checkpointNode, nil
|
||||
}
|
||||
|
||||
// isNonstandardTransaction determines whether a transaction contains any
|
||||
// scripts which are not one of the standard types.
|
||||
func isNonstandardTransaction(tx *btcutil.Tx) bool {
|
||||
// Check all of the output public key scripts for non-standard scripts.
|
||||
for _, txOut := range tx.MsgTx().TxOut {
|
||||
scriptClass := txscript.GetScriptClass(txOut.PkScript)
|
||||
if scriptClass == txscript.NonStandardTy {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCheckpointCandidate returns whether or not the passed block is a good
|
||||
// checkpoint candidate.
|
||||
//
|
||||
// The factors used to determine a good checkpoint are:
|
||||
// - The block must be in the main chain
|
||||
// - The block must be at least 'CheckpointConfirmations' blocks prior to the
|
||||
// current end of the main chain
|
||||
// - The timestamps for the blocks before and after the checkpoint must have
|
||||
// timestamps which are also before and after the checkpoint, respectively
|
||||
// (due to the median time allowance this is not always the case)
|
||||
// - The block must not contain any strange transaction such as those with
|
||||
// nonstandard scripts
|
||||
//
|
||||
// The intent is that candidates are reviewed by a developer to make the final
|
||||
// decision and then manually added to the list of checkpoints for a network.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) IsCheckpointCandidate(block *btcutil.Block) (bool, error) {
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
// A checkpoint must be in the main chain.
|
||||
node := b.index.LookupNode(block.Hash())
|
||||
if node == nil || !b.bestChain.Contains(node) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Ensure the height of the passed block and the entry for the block in
|
||||
// the main chain match. This should always be the case unless the
|
||||
// caller provided an invalid block.
|
||||
if node.height != block.Height() {
|
||||
return false, fmt.Errorf("passed block height of %d does not "+
|
||||
"match the main chain height of %d", block.Height(),
|
||||
node.height)
|
||||
}
|
||||
|
||||
// A checkpoint must be at least CheckpointConfirmations blocks
|
||||
// before the end of the main chain.
|
||||
mainChainHeight := b.bestChain.Tip().height
|
||||
if node.height > (mainChainHeight - CheckpointConfirmations) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must be have at least one block after it.
|
||||
//
|
||||
// This should always succeed since the check above already made sure it
|
||||
// is CheckpointConfirmations back, but be safe in case the constant
|
||||
// changes.
|
||||
nextNode := b.bestChain.Next(node)
|
||||
if nextNode == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must be have at least one block before it.
|
||||
if node.parent == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must have timestamps for the block and the blocks on
|
||||
// either side of it in order (due to the median time allowance this is
|
||||
// not always the case).
|
||||
prevTime := time.Unix(node.parent.timestamp, 0)
|
||||
curTime := block.MsgBlock().Header.Timestamp
|
||||
nextTime := time.Unix(nextNode.timestamp, 0)
|
||||
if prevTime.After(curTime) || nextTime.Before(curTime) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// A checkpoint must have transactions that only contain standard
|
||||
// scripts.
|
||||
for _, tx := range block.Transactions() {
|
||||
if isNonstandardTransaction(tx) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// All of the checks passed, so the block is a candidate.
|
||||
return true, nil
|
||||
}
|
|
@ -0,0 +1,586 @@
|
|||
// Copyright (c) 2015-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A variable length quantity (VLQ) is an encoding that uses an arbitrary number
|
||||
// of binary octets to represent an arbitrarily large integer. The scheme
|
||||
// employs a most significant byte (MSB) base-128 encoding where the high bit in
|
||||
// each byte indicates whether or not the byte is the final one. In addition,
|
||||
// to ensure there are no redundant encodings, an offset is subtracted every
|
||||
// time a group of 7 bits is shifted out. Therefore each integer can be
|
||||
// represented in exactly one way, and each representation stands for exactly
|
||||
// one integer.
|
||||
//
|
||||
// Another nice property of this encoding is that it provides a compact
|
||||
// representation of values that are typically used to indicate sizes. For
|
||||
// example, the values 0 - 127 are represented with a single byte, 128 - 16511
|
||||
// with two bytes, and 16512 - 2113663 with three bytes.
|
||||
//
|
||||
// While the encoding allows arbitrarily large integers, it is artificially
|
||||
// limited in this code to an unsigned 64-bit integer for efficiency purposes.
|
||||
//
|
||||
// Example encodings:
|
||||
// 0 -> [0x00]
|
||||
// 127 -> [0x7f] * Max 1-byte value
|
||||
// 128 -> [0x80 0x00]
|
||||
// 129 -> [0x80 0x01]
|
||||
// 255 -> [0x80 0x7f]
|
||||
// 256 -> [0x81 0x00]
|
||||
// 16511 -> [0xff 0x7f] * Max 2-byte value
|
||||
// 16512 -> [0x80 0x80 0x00]
|
||||
// 32895 -> [0x80 0xff 0x7f]
|
||||
// 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value
|
||||
// 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value
|
||||
// 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f]
|
||||
//
|
||||
// References:
|
||||
// https://en.wikipedia.org/wiki/Variable-length_quantity
|
||||
// http://www.codecodex.com/wiki/Variable-Length_Integers
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// serializeSizeVLQ returns the number of bytes it would take to serialize the
|
||||
// passed number as a variable-length quantity according to the format described
|
||||
// above.
|
||||
func serializeSizeVLQ(n uint64) int {
|
||||
size := 1
|
||||
for ; n > 0x7f; n = (n >> 7) - 1 {
|
||||
size++
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
// putVLQ serializes the provided number to a variable-length quantity according
|
||||
// to the format described above and returns the number of bytes of the encoded
|
||||
// value. The result is placed directly into the passed byte slice which must
|
||||
// be at least large enough to handle the number of bytes returned by the
|
||||
// serializeSizeVLQ function or it will panic.
|
||||
func putVLQ(target []byte, n uint64) int {
|
||||
offset := 0
|
||||
for ; ; offset++ {
|
||||
// The high bit is set when another byte follows.
|
||||
highBitMask := byte(0x80)
|
||||
if offset == 0 {
|
||||
highBitMask = 0x00
|
||||
}
|
||||
|
||||
target[offset] = byte(n&0x7f) | highBitMask
|
||||
if n <= 0x7f {
|
||||
break
|
||||
}
|
||||
n = (n >> 7) - 1
|
||||
}
|
||||
|
||||
// Reverse the bytes so it is MSB-encoded.
|
||||
for i, j := 0, offset; i < j; i, j = i+1, j-1 {
|
||||
target[i], target[j] = target[j], target[i]
|
||||
}
|
||||
|
||||
return offset + 1
|
||||
}
|
||||
|
||||
// deserializeVLQ deserializes the provided variable-length quantity according
|
||||
// to the format described above. It also returns the number of bytes
|
||||
// deserialized.
|
||||
func deserializeVLQ(serialized []byte) (uint64, int) {
|
||||
var n uint64
|
||||
var size int
|
||||
for _, val := range serialized {
|
||||
size++
|
||||
n = (n << 7) | uint64(val&0x7f)
|
||||
if val&0x80 != 0x80 {
|
||||
break
|
||||
}
|
||||
n++
|
||||
}
|
||||
|
||||
return n, size
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// In order to reduce the size of stored scripts, a domain specific compression
|
||||
// algorithm is used which recognizes standard scripts and stores them using
|
||||
// less bytes than the original script. The compression algorithm used here was
|
||||
// obtained from Bitcoin Core, so all credits for the algorithm go to it.
|
||||
//
|
||||
// The general serialized format is:
|
||||
//
|
||||
// <script size or type><script data>
|
||||
//
|
||||
// Field Type Size
|
||||
// script size or type VLQ variable
|
||||
// script data []byte variable
|
||||
//
|
||||
// The specific serialized format for each recognized standard script is:
|
||||
//
|
||||
// - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash>
|
||||
// - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash>
|
||||
// - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value>
|
||||
// 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use
|
||||
// 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use
|
||||
// ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported.
|
||||
//
|
||||
// Any scripts which are not recognized as one of the aforementioned standard
|
||||
// scripts are encoded using the general serialized format and encode the script
|
||||
// size as the sum of the actual size of the script and the number of special
|
||||
// cases.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// The following constants specify the special constants used to identify a
|
||||
// special script type in the domain-specific compressed script encoding.
|
||||
//
|
||||
// NOTE: This section specifically does not use iota since these values are
|
||||
// serialized and must be stable for long-term storage.
|
||||
const (
|
||||
// cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script.
|
||||
cstPayToPubKeyHash = 0
|
||||
|
||||
// cstPayToScriptHash identifies a compressed pay-to-script-hash script.
|
||||
cstPayToScriptHash = 1
|
||||
|
||||
// cstPayToPubKeyComp2 identifies a compressed pay-to-pubkey script to
|
||||
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
|
||||
// to reconstruct the full uncompressed pubkey.
|
||||
cstPayToPubKeyComp2 = 2
|
||||
|
||||
// cstPayToPubKeyComp3 identifies a compressed pay-to-pubkey script to
|
||||
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
|
||||
// to reconstruct the full uncompressed pubkey.
|
||||
cstPayToPubKeyComp3 = 3
|
||||
|
||||
// cstPayToPubKeyUncomp4 identifies a compressed pay-to-pubkey script to
|
||||
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
|
||||
// to reconstruct the full uncompressed pubkey.
|
||||
cstPayToPubKeyUncomp4 = 4
|
||||
|
||||
// cstPayToPubKeyUncomp5 identifies a compressed pay-to-pubkey script to
|
||||
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
|
||||
// to reconstruct the full uncompressed pubkey.
|
||||
cstPayToPubKeyUncomp5 = 5
|
||||
|
||||
// numSpecialScripts is the number of special scripts recognized by the
|
||||
// domain-specific script compression algorithm.
|
||||
numSpecialScripts = 6
|
||||
)
|
||||
|
||||
// isPubKeyHash returns whether or not the passed public key script is a
|
||||
// standard pay-to-pubkey-hash script along with the pubkey hash it is paying to
|
||||
// if it is.
|
||||
func isPubKeyHash(script []byte) (bool, []byte) {
|
||||
if len(script) == 25 && script[0] == txscript.OP_DUP &&
|
||||
script[1] == txscript.OP_HASH160 &&
|
||||
script[2] == txscript.OP_DATA_20 &&
|
||||
script[23] == txscript.OP_EQUALVERIFY &&
|
||||
script[24] == txscript.OP_CHECKSIG {
|
||||
|
||||
return true, script[3:23]
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// isScriptHash returns whether or not the passed public key script is a
|
||||
// standard pay-to-script-hash script along with the script hash it is paying to
|
||||
// if it is.
|
||||
func isScriptHash(script []byte) (bool, []byte) {
|
||||
if len(script) == 23 && script[0] == txscript.OP_HASH160 &&
|
||||
script[1] == txscript.OP_DATA_20 &&
|
||||
script[22] == txscript.OP_EQUAL {
|
||||
|
||||
return true, script[2:22]
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// isPubKey returns whether or not the passed public key script is a standard
|
||||
// pay-to-pubkey script that pays to a valid compressed or uncompressed public
|
||||
// key along with the serialized pubkey it is paying to if it is.
|
||||
//
|
||||
// NOTE: This function ensures the public key is actually valid since the
|
||||
// compression algorithm requires valid pubkeys. It does not support hybrid
|
||||
// pubkeys. This means that even if the script has the correct form for a
|
||||
// pay-to-pubkey script, this function will only return true when it is paying
|
||||
// to a valid compressed or uncompressed pubkey.
|
||||
func isPubKey(script []byte) (bool, []byte) {
|
||||
// Pay-to-compressed-pubkey script.
|
||||
if len(script) == 35 && script[0] == txscript.OP_DATA_33 &&
|
||||
script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 ||
|
||||
script[1] == 0x03) {
|
||||
|
||||
// Ensure the public key is valid.
|
||||
serializedPubKey := script[1:34]
|
||||
_, err := btcec.ParsePubKey(serializedPubKey, btcec.S256())
|
||||
if err == nil {
|
||||
return true, serializedPubKey
|
||||
}
|
||||
}
|
||||
|
||||
// Pay-to-uncompressed-pubkey script.
|
||||
if len(script) == 67 && script[0] == txscript.OP_DATA_65 &&
|
||||
script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 {
|
||||
|
||||
// Ensure the public key is valid.
|
||||
serializedPubKey := script[1:66]
|
||||
_, err := btcec.ParsePubKey(serializedPubKey, btcec.S256())
|
||||
if err == nil {
|
||||
return true, serializedPubKey
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// compressedScriptSize returns the number of bytes the passed script would take
|
||||
// when encoded with the domain specific compression algorithm described above.
|
||||
func compressedScriptSize(pkScript []byte) int {
|
||||
// Pay-to-pubkey-hash script.
|
||||
if valid, _ := isPubKeyHash(pkScript); valid {
|
||||
return 21
|
||||
}
|
||||
|
||||
// Pay-to-script-hash script.
|
||||
if valid, _ := isScriptHash(pkScript); valid {
|
||||
return 21
|
||||
}
|
||||
|
||||
// Pay-to-pubkey (compressed or uncompressed) script.
|
||||
if valid, _ := isPubKey(pkScript); valid {
|
||||
return 33
|
||||
}
|
||||
|
||||
// When none of the above special cases apply, encode the script as is
|
||||
// preceded by the sum of its size and the number of special cases
|
||||
// encoded as a variable length quantity.
|
||||
return serializeSizeVLQ(uint64(len(pkScript)+numSpecialScripts)) +
|
||||
len(pkScript)
|
||||
}
|
||||
|
||||
// decodeCompressedScriptSize treats the passed serialized bytes as a compressed
|
||||
// script, possibly followed by other data, and returns the number of bytes it
|
||||
// occupies taking into account the special encoding of the script size by the
|
||||
// domain specific compression algorithm described above.
|
||||
func decodeCompressedScriptSize(serialized []byte) int {
|
||||
scriptSize, bytesRead := deserializeVLQ(serialized)
|
||||
if bytesRead == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch scriptSize {
|
||||
case cstPayToPubKeyHash:
|
||||
return 21
|
||||
|
||||
case cstPayToScriptHash:
|
||||
return 21
|
||||
|
||||
case cstPayToPubKeyComp2, cstPayToPubKeyComp3, cstPayToPubKeyUncomp4,
|
||||
cstPayToPubKeyUncomp5:
|
||||
return 33
|
||||
}
|
||||
|
||||
scriptSize -= numSpecialScripts
|
||||
scriptSize += uint64(bytesRead)
|
||||
return int(scriptSize)
|
||||
}
|
||||
|
||||
// putCompressedScript compresses the passed script according to the domain
|
||||
// specific compression algorithm described above directly into the passed
|
||||
// target byte slice. The target byte slice must be at least large enough to
|
||||
// handle the number of bytes returned by the compressedScriptSize function or
|
||||
// it will panic.
|
||||
func putCompressedScript(target, pkScript []byte) int {
|
||||
// Pay-to-pubkey-hash script.
|
||||
if valid, hash := isPubKeyHash(pkScript); valid {
|
||||
target[0] = cstPayToPubKeyHash
|
||||
copy(target[1:21], hash)
|
||||
return 21
|
||||
}
|
||||
|
||||
// Pay-to-script-hash script.
|
||||
if valid, hash := isScriptHash(pkScript); valid {
|
||||
target[0] = cstPayToScriptHash
|
||||
copy(target[1:21], hash)
|
||||
return 21
|
||||
}
|
||||
|
||||
// Pay-to-pubkey (compressed or uncompressed) script.
|
||||
if valid, serializedPubKey := isPubKey(pkScript); valid {
|
||||
pubKeyFormat := serializedPubKey[0]
|
||||
switch pubKeyFormat {
|
||||
case 0x02, 0x03:
|
||||
target[0] = pubKeyFormat
|
||||
copy(target[1:33], serializedPubKey[1:33])
|
||||
return 33
|
||||
case 0x04:
|
||||
// Encode the oddness of the serialized pubkey into the
|
||||
// compressed script type.
|
||||
target[0] = pubKeyFormat | (serializedPubKey[64] & 0x01)
|
||||
copy(target[1:33], serializedPubKey[1:33])
|
||||
return 33
|
||||
}
|
||||
}
|
||||
|
||||
// When none of the above special cases apply, encode the unmodified
|
||||
// script preceded by the sum of its size and the number of special
|
||||
// cases encoded as a variable length quantity.
|
||||
encodedSize := uint64(len(pkScript) + numSpecialScripts)
|
||||
vlqSizeLen := putVLQ(target, encodedSize)
|
||||
copy(target[vlqSizeLen:], pkScript)
|
||||
return vlqSizeLen + len(pkScript)
|
||||
}
|
||||
|
||||
// decompressScript returns the original script obtained by decompressing the
|
||||
// passed compressed script according to the domain specific compression
|
||||
// algorithm described above.
|
||||
//
|
||||
// NOTE: The script parameter must already have been proven to be long enough
|
||||
// to contain the number of bytes returned by decodeCompressedScriptSize or it
|
||||
// will panic. This is acceptable since it is only an internal function.
|
||||
func decompressScript(compressedPkScript []byte) []byte {
|
||||
// In practice this function will not be called with a zero-length or
|
||||
// nil script since the nil script encoding includes the length, however
|
||||
// the code below assumes the length exists, so just return nil now if
|
||||
// the function ever ends up being called with a nil script in the
|
||||
// future.
|
||||
if len(compressedPkScript) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode the script size and examine it for the special cases.
|
||||
encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript)
|
||||
switch encodedScriptSize {
|
||||
// Pay-to-pubkey-hash script. The resulting script is:
|
||||
// <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG>
|
||||
case cstPayToPubKeyHash:
|
||||
pkScript := make([]byte, 25)
|
||||
pkScript[0] = txscript.OP_DUP
|
||||
pkScript[1] = txscript.OP_HASH160
|
||||
pkScript[2] = txscript.OP_DATA_20
|
||||
copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20])
|
||||
pkScript[23] = txscript.OP_EQUALVERIFY
|
||||
pkScript[24] = txscript.OP_CHECKSIG
|
||||
return pkScript
|
||||
|
||||
// Pay-to-script-hash script. The resulting script is:
|
||||
// <OP_HASH160><20 byte script hash><OP_EQUAL>
|
||||
case cstPayToScriptHash:
|
||||
pkScript := make([]byte, 23)
|
||||
pkScript[0] = txscript.OP_HASH160
|
||||
pkScript[1] = txscript.OP_DATA_20
|
||||
copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20])
|
||||
pkScript[22] = txscript.OP_EQUAL
|
||||
return pkScript
|
||||
|
||||
// Pay-to-compressed-pubkey script. The resulting script is:
|
||||
// <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG>
|
||||
case cstPayToPubKeyComp2, cstPayToPubKeyComp3:
|
||||
pkScript := make([]byte, 35)
|
||||
pkScript[0] = txscript.OP_DATA_33
|
||||
pkScript[1] = byte(encodedScriptSize)
|
||||
copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32])
|
||||
pkScript[34] = txscript.OP_CHECKSIG
|
||||
return pkScript
|
||||
|
||||
// Pay-to-uncompressed-pubkey script. The resulting script is:
|
||||
// <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG>
|
||||
case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5:
|
||||
// Change the leading byte to the appropriate compressed pubkey
|
||||
// identifier (0x02 or 0x03) so it can be decoded as a
|
||||
// compressed pubkey. This really should never fail since the
|
||||
// encoding ensures it is valid before compressing to this type.
|
||||
compressedKey := make([]byte, 33)
|
||||
compressedKey[0] = byte(encodedScriptSize - 2)
|
||||
copy(compressedKey[1:], compressedPkScript[1:])
|
||||
key, err := btcec.ParsePubKey(compressedKey, btcec.S256())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkScript := make([]byte, 67)
|
||||
pkScript[0] = txscript.OP_DATA_65
|
||||
copy(pkScript[1:], key.SerializeUncompressed())
|
||||
pkScript[66] = txscript.OP_CHECKSIG
|
||||
return pkScript
|
||||
}
|
||||
|
||||
// When none of the special cases apply, the script was encoded using
|
||||
// the general format, so reduce the script size by the number of
|
||||
// special cases and return the unmodified script.
|
||||
scriptSize := int(encodedScriptSize - numSpecialScripts)
|
||||
pkScript := make([]byte, scriptSize)
|
||||
copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize])
|
||||
return pkScript
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// In order to reduce the size of stored amounts, a domain specific compression
|
||||
// algorithm is used which relies on there typically being a lot of zeroes at
|
||||
// end of the amounts. The compression algorithm used here was obtained from
|
||||
// Bitcoin Core, so all credits for the algorithm go to it.
|
||||
//
|
||||
// While this is simply exchanging one uint64 for another, the resulting value
|
||||
// for typical amounts has a much smaller magnitude which results in fewer bytes
|
||||
// when encoded as variable length quantity. For example, consider the amount
|
||||
// of 0.1 BTC which is 10000000 satoshi. Encoding 10000000 as a VLQ would take
|
||||
// 4 bytes while encoding the compressed value of 8 as a VLQ only takes 1 byte.
|
||||
//
|
||||
// Essentially the compression is achieved by splitting the value into an
|
||||
// exponent in the range [0-9] and a digit in the range [1-9], when possible,
|
||||
// and encoding them in a way that can be decoded. More specifically, the
|
||||
// encoding is as follows:
|
||||
// - 0 is 0
|
||||
// - Find the exponent, e, as the largest power of 10 that evenly divides the
|
||||
// value up to a maximum of 9
|
||||
// - When e < 9, the final digit can't be 0 so store it as d and remove it by
|
||||
// dividing the value by 10 (call the result n). The encoded value is thus:
|
||||
// 1 + 10*(9*n + d-1) + e
|
||||
// - When e==9, the only thing known is the amount is not 0. The encoded value
|
||||
// is thus:
|
||||
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
|
||||
//
|
||||
// Example encodings:
|
||||
// (The numbers in parenthesis are the number of bytes when serialized as a VLQ)
|
||||
// 0 (1) -> 0 (1) * 0.00000000 BTC
|
||||
// 1000 (2) -> 4 (1) * 0.00001000 BTC
|
||||
// 10000 (2) -> 5 (1) * 0.00010000 BTC
|
||||
// 12345678 (4) -> 111111101(4) * 0.12345678 BTC
|
||||
// 50000000 (4) -> 47 (1) * 0.50000000 BTC
|
||||
// 100000000 (4) -> 9 (1) * 1.00000000 BTC
|
||||
// 500000000 (5) -> 49 (1) * 5.00000000 BTC
|
||||
// 1000000000 (5) -> 10 (1) * 10.00000000 BTC
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// compressTxOutAmount compresses the passed amount according to the domain
|
||||
// specific compression algorithm described above.
|
||||
func compressTxOutAmount(amount uint64) uint64 {
|
||||
// No need to do any work if it's zero.
|
||||
if amount == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Find the largest power of 10 (max of 9) that evenly divides the
|
||||
// value.
|
||||
exponent := uint64(0)
|
||||
for amount%10 == 0 && exponent < 9 {
|
||||
amount /= 10
|
||||
exponent++
|
||||
}
|
||||
|
||||
// The compressed result for exponents less than 9 is:
|
||||
// 1 + 10*(9*n + d-1) + e
|
||||
if exponent < 9 {
|
||||
lastDigit := amount % 10
|
||||
amount /= 10
|
||||
return 1 + 10*(9*amount+lastDigit-1) + exponent
|
||||
}
|
||||
|
||||
// The compressed result for an exponent of 9 is:
|
||||
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
|
||||
return 10 + 10*(amount-1)
|
||||
}
|
||||
|
||||
// decompressTxOutAmount returns the original amount the passed compressed
|
||||
// amount represents according to the domain specific compression algorithm
|
||||
// described above.
|
||||
func decompressTxOutAmount(amount uint64) uint64 {
|
||||
// No need to do any work if it's zero.
|
||||
if amount == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// The decompressed amount is either of the following two equations:
|
||||
// x = 1 + 10*(9*n + d - 1) + e
|
||||
// x = 1 + 10*(n - 1) + 9
|
||||
amount--
|
||||
|
||||
// The decompressed amount is now one of the following two equations:
|
||||
// x = 10*(9*n + d - 1) + e
|
||||
// x = 10*(n - 1) + 9
|
||||
exponent := amount % 10
|
||||
amount /= 10
|
||||
|
||||
// The decompressed amount is now one of the following two equations:
|
||||
// x = 9*n + d - 1 | where e < 9
|
||||
// x = n - 1 | where e = 9
|
||||
n := uint64(0)
|
||||
if exponent < 9 {
|
||||
lastDigit := amount%9 + 1
|
||||
amount /= 9
|
||||
n = amount*10 + lastDigit
|
||||
} else {
|
||||
n = amount + 1
|
||||
}
|
||||
|
||||
// Apply the exponent.
|
||||
for ; exponent > 0; exponent-- {
|
||||
n *= 10
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Compressed transaction outputs consist of an amount and a public key script
|
||||
// both compressed using the domain specific compression algorithms previously
|
||||
// described.
|
||||
//
|
||||
// The serialized format is:
|
||||
//
|
||||
// <compressed amount><compressed script>
|
||||
//
|
||||
// Field Type Size
|
||||
// compressed amount VLQ variable
|
||||
// compressed script []byte variable
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// compressedTxOutSize returns the number of bytes the passed transaction output
|
||||
// fields would take when encoded with the format described above.
|
||||
func compressedTxOutSize(amount uint64, pkScript []byte) int {
|
||||
return serializeSizeVLQ(compressTxOutAmount(amount)) +
|
||||
compressedScriptSize(pkScript)
|
||||
}
|
||||
|
||||
// putCompressedTxOut compresses the passed amount and script according to their
|
||||
// domain specific compression algorithms and encodes them directly into the
|
||||
// passed target byte slice with the format described above. The target byte
|
||||
// slice must be at least large enough to handle the number of bytes returned by
|
||||
// the compressedTxOutSize function or it will panic.
|
||||
func putCompressedTxOut(target []byte, amount uint64, pkScript []byte) int {
|
||||
offset := putVLQ(target, compressTxOutAmount(amount))
|
||||
offset += putCompressedScript(target[offset:], pkScript)
|
||||
return offset
|
||||
}
|
||||
|
||||
// decodeCompressedTxOut decodes the passed compressed txout, possibly followed
|
||||
// by other data, into its uncompressed amount and script and returns them along
|
||||
// with the number of bytes they occupied prior to decompression.
|
||||
func decodeCompressedTxOut(serialized []byte) (uint64, []byte, int, error) {
|
||||
// Deserialize the compressed amount and ensure there are bytes
|
||||
// remaining for the compressed script.
|
||||
compressedAmount, bytesRead := deserializeVLQ(serialized)
|
||||
if bytesRead >= len(serialized) {
|
||||
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
|
||||
"data after compressed amount")
|
||||
}
|
||||
|
||||
// Decode the compressed script size and ensure there are enough bytes
|
||||
// left in the slice for it.
|
||||
scriptSize := decodeCompressedScriptSize(serialized[bytesRead:])
|
||||
if len(serialized[bytesRead:]) < scriptSize {
|
||||
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
|
||||
"data after script size")
|
||||
}
|
||||
|
||||
// Decompress and return the amount and script.
|
||||
amount := decompressTxOutAmount(compressedAmount)
|
||||
script := decompressScript(serialized[bytesRead : bytesRead+scriptSize])
|
||||
return amount, script, bytesRead + scriptSize, nil
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
var (
|
||||
// bigOne is 1 represented as a big.Int. It is defined here to avoid
|
||||
// the overhead of creating it multiple times.
|
||||
bigOne = big.NewInt(1)
|
||||
|
||||
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
|
||||
// the overhead of creating it multiple times.
|
||||
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
|
||||
)
|
||||
|
||||
// HashToBig converts a chainhash.Hash into a big.Int that can be used to
|
||||
// perform math comparisons.
|
||||
func HashToBig(hash *chainhash.Hash) *big.Int {
|
||||
// A Hash is in little-endian, but the big package wants the bytes in
|
||||
// big-endian, so reverse them.
|
||||
buf := *hash
|
||||
blen := len(buf)
|
||||
for i := 0; i < blen/2; i++ {
|
||||
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
|
||||
}
|
||||
|
||||
return new(big.Int).SetBytes(buf[:])
|
||||
}
|
||||
|
||||
// CompactToBig converts a compact representation of a whole number N to an
|
||||
// unsigned 32-bit number. The representation is similar to IEEE754 floating
|
||||
// point numbers.
|
||||
//
|
||||
// Like IEEE754 floating point, there are three basic components: the sign,
|
||||
// the exponent, and the mantissa. They are broken out as follows:
|
||||
//
|
||||
// * the most significant 8 bits represent the unsigned base 256 exponent
|
||||
// * bit 23 (the 24th bit) represents the sign bit
|
||||
// * the least significant 23 bits represent the mantissa
|
||||
//
|
||||
// -------------------------------------------------
|
||||
// | Exponent | Sign | Mantissa |
|
||||
// -------------------------------------------------
|
||||
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
|
||||
// -------------------------------------------------
|
||||
//
|
||||
// The formula to calculate N is:
|
||||
// N = (-1^sign) * mantissa * 256^(exponent-3)
|
||||
//
|
||||
// This compact form is only used in bitcoin to encode unsigned 256-bit numbers
|
||||
// which represent difficulty targets, thus there really is not a need for a
|
||||
// sign bit, but it is implemented here to stay consistent with bitcoind.
|
||||
func CompactToBig(compact uint32) *big.Int {
|
||||
// Extract the mantissa, sign bit, and exponent.
|
||||
mantissa := compact & 0x007fffff
|
||||
isNegative := compact&0x00800000 != 0
|
||||
exponent := uint(compact >> 24)
|
||||
|
||||
// Since the base for the exponent is 256, the exponent can be treated
|
||||
// as the number of bytes to represent the full 256-bit number. So,
|
||||
// treat the exponent as the number of bytes and shift the mantissa
|
||||
// right or left accordingly. This is equivalent to:
|
||||
// N = mantissa * 256^(exponent-3)
|
||||
var bn *big.Int
|
||||
if exponent <= 3 {
|
||||
mantissa >>= 8 * (3 - exponent)
|
||||
bn = big.NewInt(int64(mantissa))
|
||||
} else {
|
||||
bn = big.NewInt(int64(mantissa))
|
||||
bn.Lsh(bn, 8*(exponent-3))
|
||||
}
|
||||
|
||||
// Make it negative if the sign bit is set.
|
||||
if isNegative {
|
||||
bn = bn.Neg(bn)
|
||||
}
|
||||
|
||||
return bn
|
||||
}
|
||||
|
||||
// BigToCompact converts a whole number N to a compact representation using
|
||||
// an unsigned 32-bit number. The compact representation only provides 23 bits
|
||||
// of precision, so values larger than (2^23 - 1) only encode the most
|
||||
// significant digits of the number. See CompactToBig for details.
|
||||
func BigToCompact(n *big.Int) uint32 {
|
||||
// No need to do any work if it's zero.
|
||||
if n.Sign() == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Since the base for the exponent is 256, the exponent can be treated
|
||||
// as the number of bytes. So, shift the number right or left
|
||||
// accordingly. This is equivalent to:
|
||||
// mantissa = mantissa / 256^(exponent-3)
|
||||
var mantissa uint32
|
||||
exponent := uint(len(n.Bytes()))
|
||||
if exponent <= 3 {
|
||||
mantissa = uint32(n.Bits()[0])
|
||||
mantissa <<= 8 * (3 - exponent)
|
||||
} else {
|
||||
// Use a copy to avoid modifying the caller's original number.
|
||||
tn := new(big.Int).Set(n)
|
||||
mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
|
||||
}
|
||||
|
||||
// When the mantissa already has the sign bit set, the number is too
|
||||
// large to fit into the available 23-bits, so divide the number by 256
|
||||
// and increment the exponent accordingly.
|
||||
if mantissa&0x00800000 != 0 {
|
||||
mantissa >>= 8
|
||||
exponent++
|
||||
}
|
||||
|
||||
// Pack the exponent, sign bit, and mantissa into an unsigned 32-bit
|
||||
// int and return it.
|
||||
compact := uint32(exponent<<24) | mantissa
|
||||
if n.Sign() < 0 {
|
||||
compact |= 0x00800000
|
||||
}
|
||||
return compact
|
||||
}
|
||||
|
||||
// CalcWork calculates a work value from difficulty bits. Bitcoin increases
|
||||
// the difficulty for generating a block by decreasing the value which the
|
||||
// generated hash must be less than. This difficulty target is stored in each
|
||||
// block header using a compact representation as described in the documentation
|
||||
// for CompactToBig. The main chain is selected by choosing the chain that has
|
||||
// the most proof of work (highest difficulty). Since a lower target difficulty
|
||||
// value equates to higher actual difficulty, the work value which will be
|
||||
// accumulated must be the inverse of the difficulty. Also, in order to avoid
|
||||
// potential division by zero and really small floating point numbers, the
|
||||
// result adds 1 to the denominator and multiplies the numerator by 2^256.
|
||||
func CalcWork(bits uint32) *big.Int {
|
||||
// Return a work value of zero if the passed difficulty bits represent
|
||||
// a negative number. Note this should not happen in practice with valid
|
||||
// blocks, but an invalid block could trigger it.
|
||||
difficultyNum := CompactToBig(bits)
|
||||
if difficultyNum.Sign() <= 0 {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
// (1 << 256) / (difficultyNum + 1)
|
||||
denominator := new(big.Int).Add(difficultyNum, bigOne)
|
||||
return new(big.Int).Div(oneLsh256, denominator)
|
||||
}
|
||||
|
||||
// calcEasiestDifficulty calculates the easiest possible difficulty that a block
|
||||
// can have given starting difficulty bits and a duration. It is mainly used to
|
||||
// verify that claimed proof of work by a block is sane as compared to a
|
||||
// known good checkpoint.
|
||||
func (b *BlockChain) calcEasiestDifficulty(bits uint32, duration time.Duration) uint32 {
|
||||
// Convert types used in the calculations below.
|
||||
durationVal := int64(duration / time.Second)
|
||||
adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
|
||||
|
||||
// The test network rules allow minimum difficulty blocks after more
|
||||
// than twice the desired amount of time needed to generate a block has
|
||||
// elapsed.
|
||||
if b.chainParams.ReduceMinDifficulty {
|
||||
reductionTime := int64(b.chainParams.MinDiffReductionTime /
|
||||
time.Second)
|
||||
if durationVal > reductionTime {
|
||||
return b.chainParams.PowLimitBits
|
||||
}
|
||||
}
|
||||
|
||||
// Since easier difficulty equates to higher numbers, the easiest
|
||||
// difficulty for a given duration is the largest value possible given
|
||||
// the number of retargets for the duration and starting difficulty
|
||||
// multiplied by the max adjustment factor.
|
||||
newTarget := CompactToBig(bits)
|
||||
for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
|
||||
newTarget.Mul(newTarget, adjustmentFactor)
|
||||
durationVal -= b.maxRetargetTimespan
|
||||
}
|
||||
|
||||
// Limit new value to the proof of work limit.
|
||||
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
|
||||
newTarget.Set(b.chainParams.PowLimit)
|
||||
}
|
||||
|
||||
return BigToCompact(newTarget)
|
||||
}
|
||||
|
||||
// findPrevTestNetDifficulty returns the difficulty of the previous block which
|
||||
// did not have the special testnet minimum difficulty rule applied.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) uint32 {
|
||||
// Search backwards through the chain for the last block without
|
||||
// the special rule applied.
|
||||
iterNode := startNode
|
||||
for iterNode != nil && iterNode.height%b.blocksPerRetarget != 0 &&
|
||||
iterNode.bits == b.chainParams.PowLimitBits {
|
||||
|
||||
iterNode = iterNode.parent
|
||||
}
|
||||
|
||||
// Return the found difficulty or the minimum difficulty if no
|
||||
// appropriate block was found.
|
||||
lastBits := b.chainParams.PowLimitBits
|
||||
if iterNode != nil {
|
||||
lastBits = iterNode.bits
|
||||
}
|
||||
return lastBits
|
||||
}
|
||||
|
||||
// calcNextRequiredDifficulty calculates the required difficulty for the block
|
||||
// after the passed previous block node based on the difficulty retarget rules.
|
||||
// This function differs from the exported CalcNextRequiredDifficulty in that
|
||||
// the exported version uses the current best chain as the previous block node
|
||||
// while this function accepts any block node.
|
||||
func (b *BlockChain) calcNextRequiredDifficulty(lastNode *blockNode, newBlockTime time.Time) (uint32, error) {
|
||||
// Genesis block.
|
||||
if lastNode == nil {
|
||||
return b.chainParams.PowLimitBits, nil
|
||||
}
|
||||
|
||||
// Return the previous block's difficulty requirements if this block
|
||||
// is not at a difficulty retarget interval.
|
||||
if (lastNode.height+1)%b.blocksPerRetarget != 0 {
|
||||
// For networks that support it, allow special reduction of the
|
||||
// required difficulty once too much time has elapsed without
|
||||
// mining a block.
|
||||
if b.chainParams.ReduceMinDifficulty {
|
||||
// Return minimum difficulty when more than the desired
|
||||
// amount of time has elapsed without mining a block.
|
||||
reductionTime := int64(b.chainParams.MinDiffReductionTime /
|
||||
time.Second)
|
||||
allowMinTime := lastNode.timestamp + reductionTime
|
||||
if newBlockTime.Unix() > allowMinTime {
|
||||
return b.chainParams.PowLimitBits, nil
|
||||
}
|
||||
|
||||
// The block was mined within the desired timeframe, so
|
||||
// return the difficulty for the last block which did
|
||||
// not have the special minimum difficulty rule applied.
|
||||
return b.findPrevTestNetDifficulty(lastNode), nil
|
||||
}
|
||||
|
||||
// For the main network (or any unrecognized networks), simply
|
||||
// return the previous block's difficulty requirements.
|
||||
return lastNode.bits, nil
|
||||
}
|
||||
|
||||
// Get the block node at the previous retarget (targetTimespan days
|
||||
// worth of blocks).
|
||||
firstNode := lastNode.RelativeAncestor(b.blocksPerRetarget - 1)
|
||||
if firstNode == nil {
|
||||
return 0, AssertError("unable to obtain previous retarget block")
|
||||
}
|
||||
|
||||
// Limit the amount of adjustment that can occur to the previous
|
||||
// difficulty.
|
||||
actualTimespan := lastNode.timestamp - firstNode.timestamp
|
||||
adjustedTimespan := actualTimespan
|
||||
if actualTimespan < b.minRetargetTimespan {
|
||||
adjustedTimespan = b.minRetargetTimespan
|
||||
} else if actualTimespan > b.maxRetargetTimespan {
|
||||
adjustedTimespan = b.maxRetargetTimespan
|
||||
}
|
||||
|
||||
// Calculate new target difficulty as:
|
||||
// currentDifficulty * (adjustedTimespan / targetTimespan)
|
||||
// The result uses integer division which means it will be slightly
|
||||
// rounded down. Bitcoind also uses integer division to calculate this
|
||||
// result.
|
||||
oldTarget := CompactToBig(lastNode.bits)
|
||||
newTarget := new(big.Int).Mul(oldTarget, big.NewInt(adjustedTimespan))
|
||||
targetTimeSpan := int64(b.chainParams.TargetTimespan / time.Second)
|
||||
newTarget.Div(newTarget, big.NewInt(targetTimeSpan))
|
||||
|
||||
// Limit new value to the proof of work limit.
|
||||
if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
|
||||
newTarget.Set(b.chainParams.PowLimit)
|
||||
}
|
||||
|
||||
// Log new target difficulty and return it. The new target logging is
|
||||
// intentionally converting the bits back to a number instead of using
|
||||
// newTarget since conversion to the compact representation loses
|
||||
// precision.
|
||||
newTargetBits := BigToCompact(newTarget)
|
||||
log.Debugf("Difficulty retarget at block height %d", lastNode.height+1)
|
||||
log.Debugf("Old target %08x (%064x)", lastNode.bits, oldTarget)
|
||||
log.Debugf("New target %08x (%064x)", newTargetBits, CompactToBig(newTargetBits))
|
||||
log.Debugf("Actual timespan %v, adjusted timespan %v, target timespan %v",
|
||||
time.Duration(actualTimespan)*time.Second,
|
||||
time.Duration(adjustedTimespan)*time.Second,
|
||||
b.chainParams.TargetTimespan)
|
||||
|
||||
return newTargetBits, nil
|
||||
}
|
||||
|
||||
// CalcNextRequiredDifficulty calculates the required difficulty for the block
|
||||
// after the end of the current best chain based on the difficulty retarget
|
||||
// rules.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32, error) {
|
||||
b.chainLock.Lock()
|
||||
difficulty, err := b.calcNextRequiredDifficulty(b.bestChain.Tip(), timestamp)
|
||||
b.chainLock.Unlock()
|
||||
return difficulty, err
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package blockchain implements bitcoin block handling and chain selection rules.
|
||||
|
||||
The bitcoin block handling and chain selection rules are an integral, and quite
|
||||
likely the most important, part of bitcoin. Unfortunately, at the time of
|
||||
this writing, these rules are also largely undocumented and had to be
|
||||
ascertained from the bitcoind source code. At its core, bitcoin is a
|
||||
distributed consensus of which blocks are valid and which ones will comprise the
|
||||
main block chain (public ledger) that ultimately determines accepted
|
||||
transactions, so it is extremely important that fully validating nodes agree on
|
||||
all rules.
|
||||
|
||||
At a high level, this package provides support for inserting new blocks into
|
||||
the block chain according to the aforementioned rules. It includes
|
||||
functionality such as rejecting duplicate blocks, ensuring blocks and
|
||||
transactions follow all rules, orphan handling, and best chain selection along
|
||||
with reorganization.
|
||||
|
||||
Since this package does not deal with other bitcoin specifics such as network
|
||||
communication or wallets, it provides a notification system which gives the
|
||||
caller a high level of flexibility in how they want to react to certain events
|
||||
such as orphan blocks which need their parents requested and newly connected
|
||||
main chain blocks which might result in wallet updates.
|
||||
|
||||
Bitcoin Chain Processing Overview
|
||||
|
||||
Before a block is allowed into the block chain, it must go through an intensive
|
||||
series of validation rules. The following list serves as a general outline of
|
||||
those rules to provide some intuition into what is going on under the hood, but
|
||||
is by no means exhaustive:
|
||||
|
||||
- Reject duplicate blocks
|
||||
- Perform a series of sanity checks on the block and its transactions such as
|
||||
verifying proof of work, timestamps, number and character of transactions,
|
||||
transaction amounts, script complexity, and merkle root calculations
|
||||
- Compare the block against predetermined checkpoints for expected timestamps
|
||||
and difficulty based on elapsed time since the checkpoint
|
||||
- Save the most recent orphan blocks for a limited time in case their parent
|
||||
blocks become available
|
||||
- Stop processing if the block is an orphan as the rest of the processing
|
||||
depends on the block's position within the block chain
|
||||
- Perform a series of more thorough checks that depend on the block's position
|
||||
within the block chain such as verifying block difficulties adhere to
|
||||
difficulty retarget rules, timestamps are after the median of the last
|
||||
several blocks, all transactions are finalized, checkpoint blocks match, and
|
||||
block versions are in line with the previous blocks
|
||||
- Determine how the block fits into the chain and perform different actions
|
||||
accordingly in order to ensure any side chains which have higher difficulty
|
||||
than the main chain become the new main chain
|
||||
- When a block is being connected to the main chain (either through
|
||||
reorganization of a side chain to the main chain or just extending the
|
||||
main chain), perform further checks on the block's transactions such as
|
||||
verifying transaction duplicates, script complexity for the combination of
|
||||
connected scripts, coinbase maturity, double spends, and connected
|
||||
transaction values
|
||||
- Run the transaction scripts to verify the spender is allowed to spend the
|
||||
coins
|
||||
- Insert the block into the block database
|
||||
|
||||
Errors
|
||||
|
||||
Errors returned by this package are either the raw errors provided by underlying
|
||||
calls or of type blockchain.RuleError. This allows the caller to differentiate
|
||||
between unexpected errors, such as database errors, versus errors due to rule
|
||||
violations through type assertions. In addition, callers can programmatically
|
||||
determine the specific rule violation by examining the ErrorCode field of the
|
||||
type asserted blockchain.RuleError.
|
||||
|
||||
Bitcoin Improvement Proposals
|
||||
|
||||
This package includes spec changes outlined by the following BIPs:
|
||||
|
||||
BIP0016 (https://en.bitcoin.it/wiki/BIP_0016)
|
||||
BIP0030 (https://en.bitcoin.it/wiki/BIP_0030)
|
||||
BIP0034 (https://en.bitcoin.it/wiki/BIP_0034)
|
||||
*/
|
||||
package blockchain
|
|
@ -0,0 +1,298 @@
|
|||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// DeploymentError identifies an error that indicates a deployment ID was
|
||||
// specified that does not exist.
|
||||
type DeploymentError uint32
|
||||
|
||||
// Error returns the assertion error as a human-readable string and satisfies
|
||||
// the error interface.
|
||||
func (e DeploymentError) Error() string {
|
||||
return fmt.Sprintf("deployment ID %d does not exist", uint32(e))
|
||||
}
|
||||
|
||||
// AssertError identifies an error that indicates an internal code consistency
|
||||
// issue and should be treated as a critical and unrecoverable error.
|
||||
type AssertError string
|
||||
|
||||
// Error returns the assertion error as a human-readable string and satisfies
|
||||
// the error interface.
|
||||
func (e AssertError) Error() string {
|
||||
return "assertion failed: " + string(e)
|
||||
}
|
||||
|
||||
// ErrorCode identifies a kind of error.
|
||||
type ErrorCode int
|
||||
|
||||
// These constants are used to identify a specific RuleError.
|
||||
const (
|
||||
// ErrDuplicateBlock indicates a block with the same hash already
|
||||
// exists.
|
||||
ErrDuplicateBlock ErrorCode = iota
|
||||
|
||||
// ErrBlockTooBig indicates the serialized block size exceeds the
|
||||
// maximum allowed size.
|
||||
ErrBlockTooBig
|
||||
|
||||
// ErrBlockWeightTooHigh indicates that the block's computed weight
|
||||
// metric exceeds the maximum allowed value.
|
||||
ErrBlockWeightTooHigh
|
||||
|
||||
// ErrBlockVersionTooOld indicates the block version is too old and is
|
||||
// no longer accepted since the majority of the network has upgraded
|
||||
// to a newer version.
|
||||
ErrBlockVersionTooOld
|
||||
|
||||
// ErrInvalidTime indicates the time in the passed block has a precision
|
||||
// that is more than one second. The chain consensus rules require
|
||||
// timestamps to have a maximum precision of one second.
|
||||
ErrInvalidTime
|
||||
|
||||
// ErrTimeTooOld indicates the time is either before the median time of
|
||||
// the last several blocks per the chain consensus rules or prior to the
|
||||
// most recent checkpoint.
|
||||
ErrTimeTooOld
|
||||
|
||||
// ErrTimeTooNew indicates the time is too far in the future as compared
|
||||
// the current time.
|
||||
ErrTimeTooNew
|
||||
|
||||
// ErrDifficultyTooLow indicates the difficulty for the block is lower
|
||||
// than the difficulty required by the most recent checkpoint.
|
||||
ErrDifficultyTooLow
|
||||
|
||||
// ErrUnexpectedDifficulty indicates specified bits do not align with
|
||||
// the expected value either because it doesn't match the calculated
|
||||
// valued based on difficulty regarted rules or it is out of the valid
|
||||
// range.
|
||||
ErrUnexpectedDifficulty
|
||||
|
||||
// ErrHighHash indicates the block does not hash to a value which is
|
||||
// lower than the required target difficultly.
|
||||
ErrHighHash
|
||||
|
||||
// ErrBadMerkleRoot indicates the calculated merkle root does not match
|
||||
// the expected value.
|
||||
ErrBadMerkleRoot
|
||||
|
||||
// ErrBadCheckpoint indicates a block that is expected to be at a
|
||||
// checkpoint height does not match the expected one.
|
||||
ErrBadCheckpoint
|
||||
|
||||
// ErrForkTooOld indicates a block is attempting to fork the block chain
|
||||
// before the most recent checkpoint.
|
||||
ErrForkTooOld
|
||||
|
||||
// ErrCheckpointTimeTooOld indicates a block has a timestamp before the
|
||||
// most recent checkpoint.
|
||||
ErrCheckpointTimeTooOld
|
||||
|
||||
// ErrNoTransactions indicates the block does not have a least one
|
||||
// transaction. A valid block must have at least the coinbase
|
||||
// transaction.
|
||||
ErrNoTransactions
|
||||
|
||||
// ErrNoTxInputs indicates a transaction does not have any inputs. A
|
||||
// valid transaction must have at least one input.
|
||||
ErrNoTxInputs
|
||||
|
||||
// ErrNoTxOutputs indicates a transaction does not have any outputs. A
|
||||
// valid transaction must have at least one output.
|
||||
ErrNoTxOutputs
|
||||
|
||||
// ErrTxTooBig indicates a transaction exceeds the maximum allowed size
|
||||
// when serialized.
|
||||
ErrTxTooBig
|
||||
|
||||
// ErrBadTxOutValue indicates an output value for a transaction is
|
||||
// invalid in some way such as being out of range.
|
||||
ErrBadTxOutValue
|
||||
|
||||
// ErrDuplicateTxInputs indicates a transaction references the same
|
||||
// input more than once.
|
||||
ErrDuplicateTxInputs
|
||||
|
||||
// ErrBadTxInput indicates a transaction input is invalid in some way
|
||||
// such as referencing a previous transaction outpoint which is out of
|
||||
// range or not referencing one at all.
|
||||
ErrBadTxInput
|
||||
|
||||
// ErrMissingTxOut indicates a transaction output referenced by an input
|
||||
// either does not exist or has already been spent.
|
||||
ErrMissingTxOut
|
||||
|
||||
// ErrUnfinalizedTx indicates a transaction has not been finalized.
|
||||
// A valid block may only contain finalized transactions.
|
||||
ErrUnfinalizedTx
|
||||
|
||||
// ErrDuplicateTx indicates a block contains an identical transaction
|
||||
// (or at least two transactions which hash to the same value). A
|
||||
// valid block may only contain unique transactions.
|
||||
ErrDuplicateTx
|
||||
|
||||
// ErrOverwriteTx indicates a block contains a transaction that has
|
||||
// the same hash as a previous transaction which has not been fully
|
||||
// spent.
|
||||
ErrOverwriteTx
|
||||
|
||||
// ErrImmatureSpend indicates a transaction is attempting to spend a
|
||||
// coinbase that has not yet reached the required maturity.
|
||||
ErrImmatureSpend
|
||||
|
||||
// ErrSpendTooHigh indicates a transaction is attempting to spend more
|
||||
// value than the sum of all of its inputs.
|
||||
ErrSpendTooHigh
|
||||
|
||||
// ErrBadFees indicates the total fees for a block are invalid due to
|
||||
// exceeding the maximum possible value.
|
||||
ErrBadFees
|
||||
|
||||
// ErrTooManySigOps indicates the total number of signature operations
|
||||
// for a transaction or block exceed the maximum allowed limits.
|
||||
ErrTooManySigOps
|
||||
|
||||
// ErrFirstTxNotCoinbase indicates the first transaction in a block
|
||||
// is not a coinbase transaction.
|
||||
ErrFirstTxNotCoinbase
|
||||
|
||||
// ErrMultipleCoinbases indicates a block contains more than one
|
||||
// coinbase transaction.
|
||||
ErrMultipleCoinbases
|
||||
|
||||
// ErrBadCoinbaseScriptLen indicates the length of the signature script
|
||||
// for a coinbase transaction is not within the valid range.
|
||||
ErrBadCoinbaseScriptLen
|
||||
|
||||
// ErrBadCoinbaseValue indicates the amount of a coinbase value does
|
||||
// not match the expected value of the subsidy plus the sum of all fees.
|
||||
ErrBadCoinbaseValue
|
||||
|
||||
// ErrMissingCoinbaseHeight indicates the coinbase transaction for a
|
||||
// block does not start with the serialized block block height as
|
||||
// required for version 2 and higher blocks.
|
||||
ErrMissingCoinbaseHeight
|
||||
|
||||
// ErrBadCoinbaseHeight indicates the serialized block height in the
|
||||
// coinbase transaction for version 2 and higher blocks does not match
|
||||
// the expected value.
|
||||
ErrBadCoinbaseHeight
|
||||
|
||||
// ErrScriptMalformed indicates a transaction script is malformed in
|
||||
// some way. For example, it might be longer than the maximum allowed
|
||||
// length or fail to parse.
|
||||
ErrScriptMalformed
|
||||
|
||||
// ErrScriptValidation indicates the result of executing transaction
|
||||
// script failed. The error covers any failure when executing scripts
|
||||
// such signature verification failures and execution past the end of
|
||||
// the stack.
|
||||
ErrScriptValidation
|
||||
|
||||
// ErrUnexpectedWitness indicates that a block includes transactions
|
||||
// with witness data, but doesn't also have a witness commitment within
|
||||
// the coinbase transaction.
|
||||
ErrUnexpectedWitness
|
||||
|
||||
// ErrInvalidWitnessCommitment indicates that a block's witness
|
||||
// commitment is not well formed.
|
||||
ErrInvalidWitnessCommitment
|
||||
|
||||
// ErrWitnessCommitmentMismatch indicates that the witness commitment
|
||||
// included in the block's coinbase transaction doesn't match the
|
||||
// manually computed witness commitment.
|
||||
ErrWitnessCommitmentMismatch
|
||||
|
||||
// ErrPreviousBlockUnknown indicates that the previous block is not known.
|
||||
ErrPreviousBlockUnknown
|
||||
|
||||
// ErrInvalidAncestorBlock indicates that an ancestor of this block has
|
||||
// already failed validation.
|
||||
ErrInvalidAncestorBlock
|
||||
|
||||
// ErrPrevBlockNotBest indicates that the block's previous block is not the
|
||||
// current chain tip. This is not a block validation rule, but is required
|
||||
// for block proposals submitted via getblocktemplate RPC.
|
||||
ErrPrevBlockNotBest
|
||||
)
|
||||
|
||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||
var errorCodeStrings = map[ErrorCode]string{
|
||||
ErrDuplicateBlock: "ErrDuplicateBlock",
|
||||
ErrBlockTooBig: "ErrBlockTooBig",
|
||||
ErrBlockVersionTooOld: "ErrBlockVersionTooOld",
|
||||
ErrBlockWeightTooHigh: "ErrBlockWeightTooHigh",
|
||||
ErrInvalidTime: "ErrInvalidTime",
|
||||
ErrTimeTooOld: "ErrTimeTooOld",
|
||||
ErrTimeTooNew: "ErrTimeTooNew",
|
||||
ErrDifficultyTooLow: "ErrDifficultyTooLow",
|
||||
ErrUnexpectedDifficulty: "ErrUnexpectedDifficulty",
|
||||
ErrHighHash: "ErrHighHash",
|
||||
ErrBadMerkleRoot: "ErrBadMerkleRoot",
|
||||
ErrBadCheckpoint: "ErrBadCheckpoint",
|
||||
ErrForkTooOld: "ErrForkTooOld",
|
||||
ErrCheckpointTimeTooOld: "ErrCheckpointTimeTooOld",
|
||||
ErrNoTransactions: "ErrNoTransactions",
|
||||
ErrNoTxInputs: "ErrNoTxInputs",
|
||||
ErrNoTxOutputs: "ErrNoTxOutputs",
|
||||
ErrTxTooBig: "ErrTxTooBig",
|
||||
ErrBadTxOutValue: "ErrBadTxOutValue",
|
||||
ErrDuplicateTxInputs: "ErrDuplicateTxInputs",
|
||||
ErrBadTxInput: "ErrBadTxInput",
|
||||
ErrMissingTxOut: "ErrMissingTxOut",
|
||||
ErrUnfinalizedTx: "ErrUnfinalizedTx",
|
||||
ErrDuplicateTx: "ErrDuplicateTx",
|
||||
ErrOverwriteTx: "ErrOverwriteTx",
|
||||
ErrImmatureSpend: "ErrImmatureSpend",
|
||||
ErrSpendTooHigh: "ErrSpendTooHigh",
|
||||
ErrBadFees: "ErrBadFees",
|
||||
ErrTooManySigOps: "ErrTooManySigOps",
|
||||
ErrFirstTxNotCoinbase: "ErrFirstTxNotCoinbase",
|
||||
ErrMultipleCoinbases: "ErrMultipleCoinbases",
|
||||
ErrBadCoinbaseScriptLen: "ErrBadCoinbaseScriptLen",
|
||||
ErrBadCoinbaseValue: "ErrBadCoinbaseValue",
|
||||
ErrMissingCoinbaseHeight: "ErrMissingCoinbaseHeight",
|
||||
ErrBadCoinbaseHeight: "ErrBadCoinbaseHeight",
|
||||
ErrScriptMalformed: "ErrScriptMalformed",
|
||||
ErrScriptValidation: "ErrScriptValidation",
|
||||
ErrUnexpectedWitness: "ErrUnexpectedWitness",
|
||||
ErrInvalidWitnessCommitment: "ErrInvalidWitnessCommitment",
|
||||
ErrWitnessCommitmentMismatch: "ErrWitnessCommitmentMismatch",
|
||||
ErrPreviousBlockUnknown: "ErrPreviousBlockUnknown",
|
||||
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
|
||||
ErrPrevBlockNotBest: "ErrPrevBlockNotBest",
|
||||
}
|
||||
|
||||
// String returns the ErrorCode as a human-readable name.
|
||||
func (e ErrorCode) String() string {
|
||||
if s := errorCodeStrings[e]; s != "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
|
||||
}
|
||||
|
||||
// RuleError identifies a rule violation. It is used to indicate that
|
||||
// processing of a block or transaction failed due to one of the many validation
|
||||
// rules. The caller can use type assertions to determine if a failure was
|
||||
// specifically due to a rule violation and access the ErrorCode field to
|
||||
// ascertain the specific reason for the rule violation.
|
||||
type RuleError struct {
|
||||
ErrorCode ErrorCode // Describes the kind of error
|
||||
Description string // Human readable description of the issue
|
||||
}
|
||||
|
||||
// Error satisfies the error interface and prints human-readable errors.
|
||||
func (e RuleError) Error() string {
|
||||
return e.Description
|
||||
}
|
||||
|
||||
// ruleError creates an RuleError given a set of arguments.
|
||||
func ruleError(c ErrorCode, desc string) RuleError {
|
||||
return RuleError{ErrorCode: c, Description: desc}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btclog"
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
DisableLog()
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until UseLogger is called.
|
||||
func DisableLog() {
|
||||
log = btclog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxAllowedOffsetSeconds is the maximum number of seconds in either
|
||||
// direction that local clock will be adjusted. When the median time
|
||||
// of the network is outside of this range, no offset will be applied.
|
||||
maxAllowedOffsetSecs = 70 * 60 // 1 hour 10 minutes
|
||||
|
||||
// similarTimeSecs is the number of seconds in either direction from the
|
||||
// local clock that is used to determine that it is likely wrong and
|
||||
// hence to show a warning.
|
||||
similarTimeSecs = 5 * 60 // 5 minutes
|
||||
)
|
||||
|
||||
var (
|
||||
// maxMedianTimeEntries is the maximum number of entries allowed in the
|
||||
// median time data. This is a variable as opposed to a constant so the
|
||||
// test code can modify it.
|
||||
maxMedianTimeEntries = 200
|
||||
)
|
||||
|
||||
// MedianTimeSource provides a mechanism to add several time samples which are
|
||||
// used to determine a median time which is then used as an offset to the local
|
||||
// clock.
|
||||
type MedianTimeSource interface {
|
||||
// AdjustedTime returns the current time adjusted by the median time
|
||||
// offset as calculated from the time samples added by AddTimeSample.
|
||||
AdjustedTime() time.Time
|
||||
|
||||
// AddTimeSample adds a time sample that is used when determining the
|
||||
// median time of the added samples.
|
||||
AddTimeSample(id string, timeVal time.Time)
|
||||
|
||||
// Offset returns the number of seconds to adjust the local clock based
|
||||
// upon the median of the time samples added by AddTimeData.
|
||||
Offset() time.Duration
|
||||
}
|
||||
|
||||
// int64Sorter implements sort.Interface to allow a slice of 64-bit integers to
|
||||
// be sorted.
|
||||
type int64Sorter []int64
|
||||
|
||||
// Len returns the number of 64-bit integers in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s int64Sorter) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps the 64-bit integers at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s int64Sorter) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less returns whether the 64-bit integer with index i should sort before the
|
||||
// 64-bit integer with index j. It is part of the sort.Interface
|
||||
// implementation.
|
||||
func (s int64Sorter) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
||||
|
||||
// medianTime provides an implementation of the MedianTimeSource interface.
|
||||
// It is limited to maxMedianTimeEntries includes the same buggy behavior as
|
||||
// the time offset mechanism in Bitcoin Core. This is necessary because it is
|
||||
// used in the consensus code.
|
||||
type medianTime struct {
|
||||
mtx sync.Mutex
|
||||
knownIDs map[string]struct{}
|
||||
offsets []int64
|
||||
offsetSecs int64
|
||||
invalidTimeChecked bool
|
||||
}
|
||||
|
||||
// Ensure the medianTime type implements the MedianTimeSource interface.
|
||||
var _ MedianTimeSource = (*medianTime)(nil)
|
||||
|
||||
// AdjustedTime returns the current time adjusted by the median time offset as
|
||||
// calculated from the time samples added by AddTimeSample.
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// MedianTimeSource interface implementation.
|
||||
func (m *medianTime) AdjustedTime() time.Time {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
// Limit the adjusted time to 1 second precision.
|
||||
now := time.Unix(time.Now().Unix(), 0)
|
||||
return now.Add(time.Duration(m.offsetSecs) * time.Second)
|
||||
}
|
||||
|
||||
// AddTimeSample adds a time sample that is used when determining the median
|
||||
// time of the added samples.
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// MedianTimeSource interface implementation.
|
||||
func (m *medianTime) AddTimeSample(sourceID string, timeVal time.Time) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
// Don't add time data from the same source.
|
||||
if _, exists := m.knownIDs[sourceID]; exists {
|
||||
return
|
||||
}
|
||||
m.knownIDs[sourceID] = struct{}{}
|
||||
|
||||
// Truncate the provided offset to seconds and append it to the slice
|
||||
// of offsets while respecting the maximum number of allowed entries by
|
||||
// replacing the oldest entry with the new entry once the maximum number
|
||||
// of entries is reached.
|
||||
now := time.Unix(time.Now().Unix(), 0)
|
||||
offsetSecs := int64(timeVal.Sub(now).Seconds())
|
||||
numOffsets := len(m.offsets)
|
||||
if numOffsets == maxMedianTimeEntries && maxMedianTimeEntries > 0 {
|
||||
m.offsets = m.offsets[1:]
|
||||
numOffsets--
|
||||
}
|
||||
m.offsets = append(m.offsets, offsetSecs)
|
||||
numOffsets++
|
||||
|
||||
// Sort the offsets so the median can be obtained as needed later.
|
||||
sortedOffsets := make([]int64, numOffsets)
|
||||
copy(sortedOffsets, m.offsets)
|
||||
sort.Sort(int64Sorter(sortedOffsets))
|
||||
|
||||
offsetDuration := time.Duration(offsetSecs) * time.Second
|
||||
log.Debugf("Added time sample of %v (total: %v)", offsetDuration,
|
||||
numOffsets)
|
||||
|
||||
// NOTE: The following code intentionally has a bug to mirror the
|
||||
// buggy behavior in Bitcoin Core since the median time is used in the
|
||||
// consensus rules.
|
||||
//
|
||||
// In particular, the offset is only updated when the number of entries
|
||||
// is odd, but the max number of entries is 200, an even number. Thus,
|
||||
// the offset will never be updated again once the max number of entries
|
||||
// is reached.
|
||||
|
||||
// The median offset is only updated when there are enough offsets and
|
||||
// the number of offsets is odd so the middle value is the true median.
|
||||
// Thus, there is nothing to do when those conditions are not met.
|
||||
if numOffsets < 5 || numOffsets&0x01 != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// At this point the number of offsets in the list is odd, so the
|
||||
// middle value of the sorted offsets is the median.
|
||||
median := sortedOffsets[numOffsets/2]
|
||||
|
||||
// Set the new offset when the median offset is within the allowed
|
||||
// offset range.
|
||||
if math.Abs(float64(median)) < maxAllowedOffsetSecs {
|
||||
m.offsetSecs = median
|
||||
} else {
|
||||
// The median offset of all added time data is larger than the
|
||||
// maximum allowed offset, so don't use an offset. This
|
||||
// effectively limits how far the local clock can be skewed.
|
||||
m.offsetSecs = 0
|
||||
|
||||
if !m.invalidTimeChecked {
|
||||
m.invalidTimeChecked = true
|
||||
|
||||
// Find if any time samples have a time that is close
|
||||
// to the local time.
|
||||
var remoteHasCloseTime bool
|
||||
for _, offset := range sortedOffsets {
|
||||
if math.Abs(float64(offset)) < similarTimeSecs {
|
||||
remoteHasCloseTime = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if none of the time samples are close.
|
||||
if !remoteHasCloseTime {
|
||||
log.Warnf("Please check your date and time " +
|
||||
"are correct! btcd will not work " +
|
||||
"properly with an invalid time")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
medianDuration := time.Duration(m.offsetSecs) * time.Second
|
||||
log.Debugf("New time offset: %v", medianDuration)
|
||||
}
|
||||
|
||||
// Offset returns the number of seconds to adjust the local clock based upon the
|
||||
// median of the time samples added by AddTimeData.
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// MedianTimeSource interface implementation.
|
||||
func (m *medianTime) Offset() time.Duration {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
return time.Duration(m.offsetSecs) * time.Second
|
||||
}
|
||||
|
||||
// NewMedianTime returns a new instance of concurrency-safe implementation of
|
||||
// the MedianTimeSource interface. The returned implementation contains the
|
||||
// rules necessary for proper time handling in the chain consensus rules and
|
||||
// expects the time samples to be added from the timestamp field of the version
|
||||
// message received from remote peers that successfully connect and negotiate.
|
||||
func NewMedianTime() MedianTimeSource {
|
||||
return &medianTime{
|
||||
knownIDs: make(map[string]struct{}),
|
||||
offsets: make([]int64, 0, maxMedianTimeEntries),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// CoinbaseWitnessDataLen is the required length of the only element within
|
||||
// the coinbase's witness data if the coinbase transaction contains a
|
||||
// witness commitment.
|
||||
CoinbaseWitnessDataLen = 32
|
||||
|
||||
// CoinbaseWitnessPkScriptLength is the length of the public key script
|
||||
// containing an OP_RETURN, the WitnessMagicBytes, and the witness
|
||||
// commitment itself. In order to be a valid candidate for the output
|
||||
// containing the witness commitment
|
||||
CoinbaseWitnessPkScriptLength = 38
|
||||
)
|
||||
|
||||
var (
|
||||
// WitnessMagicBytes is the prefix marker within the public key script
|
||||
// of a coinbase output to indicate that this output holds the witness
|
||||
// commitment for a block.
|
||||
WitnessMagicBytes = []byte{
|
||||
txscript.OP_RETURN,
|
||||
txscript.OP_DATA_36,
|
||||
0xaa,
|
||||
0x21,
|
||||
0xa9,
|
||||
0xed,
|
||||
}
|
||||
)
|
||||
|
||||
// nextPowerOfTwo returns the next highest power of two from a given number if
|
||||
// it is not already a power of two. This is a helper function used during the
|
||||
// calculation of a merkle tree.
|
||||
func nextPowerOfTwo(n int) int {
|
||||
// Return the number if it's already a power of 2.
|
||||
if n&(n-1) == 0 {
|
||||
return n
|
||||
}
|
||||
|
||||
// Figure out and return the next power of two.
|
||||
exponent := uint(math.Log2(float64(n))) + 1
|
||||
return 1 << exponent // 2^exponent
|
||||
}
|
||||
|
||||
// HashMerkleBranches takes two hashes, treated as the left and right tree
|
||||
// nodes, and returns the hash of their concatenation. This is a helper
|
||||
// function used to aid in the generation of a merkle tree.
|
||||
func HashMerkleBranches(left *chainhash.Hash, right *chainhash.Hash) *chainhash.Hash {
|
||||
// Concatenate the left and right nodes.
|
||||
var hash [chainhash.HashSize * 2]byte
|
||||
copy(hash[:chainhash.HashSize], left[:])
|
||||
copy(hash[chainhash.HashSize:], right[:])
|
||||
|
||||
newHash := chainhash.DoubleHashH(hash[:])
|
||||
return &newHash
|
||||
}
|
||||
|
||||
// BuildMerkleTreeStore creates a merkle tree from a slice of transactions,
|
||||
// stores it using a linear array, and returns a slice of the backing array. A
|
||||
// linear array was chosen as opposed to an actual tree structure since it uses
|
||||
// about half as much memory. The following describes a merkle tree and how it
|
||||
// is stored in a linear array.
|
||||
//
|
||||
// A merkle tree is a tree in which every non-leaf node is the hash of its
|
||||
// children nodes. A diagram depicting how this works for bitcoin transactions
|
||||
// where h(x) is a double sha256 follows:
|
||||
//
|
||||
// root = h1234 = h(h12 + h34)
|
||||
// / \
|
||||
// h12 = h(h1 + h2) h34 = h(h3 + h4)
|
||||
// / \ / \
|
||||
// h1 = h(tx1) h2 = h(tx2) h3 = h(tx3) h4 = h(tx4)
|
||||
//
|
||||
// The above stored as a linear array is as follows:
|
||||
//
|
||||
// [h1 h2 h3 h4 h12 h34 root]
|
||||
//
|
||||
// As the above shows, the merkle root is always the last element in the array.
|
||||
//
|
||||
// The number of inputs is not always a power of two which results in a
|
||||
// balanced tree structure as above. In that case, parent nodes with no
|
||||
// children are also zero and parent nodes with only a single left node
|
||||
// are calculated by concatenating the left node with itself before hashing.
|
||||
// Since this function uses nodes that are pointers to the hashes, empty nodes
|
||||
// will be nil.
|
||||
//
|
||||
// The additional bool parameter indicates if we are generating the merkle tree
|
||||
// using witness transaction id's rather than regular transaction id's. This
|
||||
// also presents an additional case wherein the wtxid of the coinbase transaction
|
||||
// is the zeroHash.
|
||||
func BuildMerkleTreeStore(transactions []*btcutil.Tx, witness bool) []*chainhash.Hash {
|
||||
// Calculate how many entries are required to hold the binary merkle
|
||||
// tree as a linear array and create an array of that size.
|
||||
nextPoT := nextPowerOfTwo(len(transactions))
|
||||
arraySize := nextPoT*2 - 1
|
||||
merkles := make([]*chainhash.Hash, arraySize)
|
||||
|
||||
// Create the base transaction hashes and populate the array with them.
|
||||
for i, tx := range transactions {
|
||||
// If we're computing a witness merkle root, instead of the
|
||||
// regular txid, we use the modified wtxid which includes a
|
||||
// transaction's witness data within the digest. Additionally,
|
||||
// the coinbase's wtxid is all zeroes.
|
||||
switch {
|
||||
case witness && i == 0:
|
||||
var zeroHash chainhash.Hash
|
||||
merkles[i] = &zeroHash
|
||||
case witness:
|
||||
wSha := tx.MsgTx().WitnessHash()
|
||||
merkles[i] = &wSha
|
||||
default:
|
||||
merkles[i] = tx.Hash()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Start the array offset after the last transaction and adjusted to the
|
||||
// next power of two.
|
||||
offset := nextPoT
|
||||
for i := 0; i < arraySize-1; i += 2 {
|
||||
switch {
|
||||
// When there is no left child node, the parent is nil too.
|
||||
case merkles[i] == nil:
|
||||
merkles[offset] = nil
|
||||
|
||||
// When there is no right child, the parent is generated by
|
||||
// hashing the concatenation of the left child with itself.
|
||||
case merkles[i+1] == nil:
|
||||
newHash := HashMerkleBranches(merkles[i], merkles[i])
|
||||
merkles[offset] = newHash
|
||||
|
||||
// The normal case sets the parent node to the double sha256
|
||||
// of the concatentation of the left and right children.
|
||||
default:
|
||||
newHash := HashMerkleBranches(merkles[i], merkles[i+1])
|
||||
merkles[offset] = newHash
|
||||
}
|
||||
offset++
|
||||
}
|
||||
|
||||
return merkles
|
||||
}
|
||||
|
||||
// ExtractWitnessCommitment attempts to locate, and return the witness
|
||||
// commitment for a block. The witness commitment is of the form:
|
||||
// SHA256(witness root || witness nonce). The function additionally returns a
|
||||
// boolean indicating if the witness root was located within any of the txOut's
|
||||
// in the passed transaction. The witness commitment is stored as the data push
|
||||
// for an OP_RETURN with special magic bytes to aide in location.
|
||||
func ExtractWitnessCommitment(tx *btcutil.Tx) ([]byte, bool) {
|
||||
// The witness commitment *must* be located within one of the coinbase
|
||||
// transaction's outputs.
|
||||
if !IsCoinBase(tx) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
msgTx := tx.MsgTx()
|
||||
for i := len(msgTx.TxOut) - 1; i >= 0; i-- {
|
||||
// The public key script that contains the witness commitment
|
||||
// must shared a prefix with the WitnessMagicBytes, and be at
|
||||
// least 38 bytes.
|
||||
pkScript := msgTx.TxOut[i].PkScript
|
||||
if len(pkScript) >= CoinbaseWitnessPkScriptLength &&
|
||||
bytes.HasPrefix(pkScript, WitnessMagicBytes) {
|
||||
|
||||
// The witness commitment itself is a 32-byte hash
|
||||
// directly after the WitnessMagicBytes. The remaining
|
||||
// bytes beyond the 38th byte currently have no consensus
|
||||
// meaning.
|
||||
start := len(WitnessMagicBytes)
|
||||
end := CoinbaseWitnessPkScriptLength
|
||||
return msgTx.TxOut[i].PkScript[start:end], true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// ValidateWitnessCommitment validates the witness commitment (if any) found
|
||||
// within the coinbase transaction of the passed block.
|
||||
func ValidateWitnessCommitment(blk *btcutil.Block) error {
|
||||
// If the block doesn't have any transactions at all, then we won't be
|
||||
// able to extract a commitment from the non-existent coinbase
|
||||
// transaction. So we exit early here.
|
||||
if len(blk.Transactions()) == 0 {
|
||||
str := "cannot validate witness commitment of block without " +
|
||||
"transactions"
|
||||
return ruleError(ErrNoTransactions, str)
|
||||
}
|
||||
|
||||
coinbaseTx := blk.Transactions()[0]
|
||||
if len(coinbaseTx.MsgTx().TxIn) == 0 {
|
||||
return ruleError(ErrNoTxInputs, "transaction has no inputs")
|
||||
}
|
||||
|
||||
witnessCommitment, witnessFound := ExtractWitnessCommitment(coinbaseTx)
|
||||
|
||||
// If we can't find a witness commitment in any of the coinbase's
|
||||
// outputs, then the block MUST NOT contain any transactions with
|
||||
// witness data.
|
||||
if !witnessFound {
|
||||
for _, tx := range blk.Transactions() {
|
||||
msgTx := tx.MsgTx()
|
||||
if msgTx.HasWitness() {
|
||||
str := fmt.Sprintf("block contains transaction with witness" +
|
||||
" data, yet no witness commitment present")
|
||||
return ruleError(ErrUnexpectedWitness, str)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// At this point the block contains a witness commitment, so the
|
||||
// coinbase transaction MUST have exactly one witness element within
|
||||
// its witness data and that element must be exactly
|
||||
// CoinbaseWitnessDataLen bytes.
|
||||
coinbaseWitness := coinbaseTx.MsgTx().TxIn[0].Witness
|
||||
if len(coinbaseWitness) != 1 {
|
||||
str := fmt.Sprintf("the coinbase transaction has %d items in "+
|
||||
"its witness stack when only one is allowed",
|
||||
len(coinbaseWitness))
|
||||
return ruleError(ErrInvalidWitnessCommitment, str)
|
||||
}
|
||||
witnessNonce := coinbaseWitness[0]
|
||||
if len(witnessNonce) != CoinbaseWitnessDataLen {
|
||||
str := fmt.Sprintf("the coinbase transaction witness nonce "+
|
||||
"has %d bytes when it must be %d bytes",
|
||||
len(witnessNonce), CoinbaseWitnessDataLen)
|
||||
return ruleError(ErrInvalidWitnessCommitment, str)
|
||||
}
|
||||
|
||||
// Finally, with the preliminary checks out of the way, we can check if
|
||||
// the extracted witnessCommitment is equal to:
|
||||
// SHA256(witnessMerkleRoot || witnessNonce). Where witnessNonce is the
|
||||
// coinbase transaction's only witness item.
|
||||
witnessMerkleTree := BuildMerkleTreeStore(blk.Transactions(), true)
|
||||
witnessMerkleRoot := witnessMerkleTree[len(witnessMerkleTree)-1]
|
||||
|
||||
var witnessPreimage [chainhash.HashSize * 2]byte
|
||||
copy(witnessPreimage[:], witnessMerkleRoot[:])
|
||||
copy(witnessPreimage[chainhash.HashSize:], witnessNonce)
|
||||
|
||||
computedCommitment := chainhash.DoubleHashB(witnessPreimage[:])
|
||||
if !bytes.Equal(computedCommitment, witnessCommitment) {
|
||||
str := fmt.Sprintf("witness commitment does not match: "+
|
||||
"computed %v, coinbase includes %v", computedCommitment,
|
||||
witnessCommitment)
|
||||
return ruleError(ErrWitnessCommitmentMismatch, str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// NotificationType represents the type of a notification message.
|
||||
type NotificationType int
|
||||
|
||||
// NotificationCallback is used for a caller to provide a callback for
|
||||
// notifications about various chain events.
|
||||
type NotificationCallback func(*Notification)
|
||||
|
||||
// Constants for the type of a notification message.
|
||||
const (
|
||||
// NTBlockAccepted indicates the associated block was accepted into
|
||||
// the block chain. Note that this does not necessarily mean it was
|
||||
// added to the main chain. For that, use NTBlockConnected.
|
||||
NTBlockAccepted NotificationType = iota
|
||||
|
||||
// NTBlockConnected indicates the associated block was connected to the
|
||||
// main chain.
|
||||
NTBlockConnected
|
||||
|
||||
// NTBlockDisconnected indicates the associated block was disconnected
|
||||
// from the main chain.
|
||||
NTBlockDisconnected
|
||||
)
|
||||
|
||||
// notificationTypeStrings is a map of notification types back to their constant
|
||||
// names for pretty printing.
|
||||
var notificationTypeStrings = map[NotificationType]string{
|
||||
NTBlockAccepted: "NTBlockAccepted",
|
||||
NTBlockConnected: "NTBlockConnected",
|
||||
NTBlockDisconnected: "NTBlockDisconnected",
|
||||
}
|
||||
|
||||
// String returns the NotificationType in human-readable form.
|
||||
func (n NotificationType) String() string {
|
||||
if s, ok := notificationTypeStrings[n]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown Notification Type (%d)", int(n))
|
||||
}
|
||||
|
||||
// Notification defines notification that is sent to the caller via the callback
|
||||
// function provided during the call to New and consists of a notification type
|
||||
// as well as associated data that depends on the type as follows:
|
||||
// - NTBlockAccepted: *btcutil.Block
|
||||
// - NTBlockConnected: *btcutil.Block
|
||||
// - NTBlockDisconnected: *btcutil.Block
|
||||
type Notification struct {
|
||||
Type NotificationType
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// Subscribe to block chain notifications. Registers a callback to be executed
|
||||
// when various events take place. See the documentation on Notification and
|
||||
// NotificationType for details on the types and contents of notifications.
|
||||
func (b *BlockChain) Subscribe(callback NotificationCallback) {
|
||||
b.notificationsLock.Lock()
|
||||
b.notifications = append(b.notifications, callback)
|
||||
b.notificationsLock.Unlock()
|
||||
}
|
||||
|
||||
// sendNotification sends a notification with the passed type and data if the
|
||||
// caller requested notifications by providing a callback function in the call
|
||||
// to New.
|
||||
func (b *BlockChain) sendNotification(typ NotificationType, data interface{}) {
|
||||
// Generate and send the notification.
|
||||
n := Notification{Type: typ, Data: data}
|
||||
b.notificationsLock.RLock()
|
||||
for _, callback := range b.notifications {
|
||||
callback(&n)
|
||||
}
|
||||
b.notificationsLock.RUnlock()
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// BehaviorFlags is a bitmask defining tweaks to the normal behavior when
|
||||
// performing chain processing and consensus rules checks.
|
||||
type BehaviorFlags uint32
|
||||
|
||||
const (
|
||||
// BFFastAdd may be set to indicate that several checks can be avoided
|
||||
// for the block since it is already known to fit into the chain due to
|
||||
// already proving it correct links into the chain up to a known
|
||||
// checkpoint. This is primarily used for headers-first mode.
|
||||
BFFastAdd BehaviorFlags = 1 << iota
|
||||
|
||||
// BFNoPoWCheck may be set to indicate the proof of work check which
|
||||
// ensures a block hashes to a value less than the required target will
|
||||
// not be performed.
|
||||
BFNoPoWCheck
|
||||
|
||||
// BFNone is a convenience value to specifically indicate no flags.
|
||||
BFNone BehaviorFlags = 0
|
||||
)
|
||||
|
||||
// blockExists determines whether a block with the given hash exists either in
|
||||
// the main chain or any side chains.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) blockExists(hash *chainhash.Hash) (bool, error) {
|
||||
// Check block index first (could be main chain or side chain blocks).
|
||||
if b.index.HaveBlock(hash) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Check in the database.
|
||||
var exists bool
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
exists, err = dbTx.HasBlock(hash)
|
||||
if err != nil || !exists {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ignore side chain blocks in the database. This is necessary
|
||||
// because there is not currently any record of the associated
|
||||
// block index data such as its block height, so it's not yet
|
||||
// possible to efficiently load the block and do anything useful
|
||||
// with it.
|
||||
//
|
||||
// Ultimately the entire block index should be serialized
|
||||
// instead of only the current main chain so it can be consulted
|
||||
// directly.
|
||||
_, err = dbFetchHeightByHash(dbTx, hash)
|
||||
if isNotInMainChainErr(err) {
|
||||
exists = false
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
return exists, err
|
||||
}
|
||||
|
||||
// processOrphans determines if there are any orphans which depend on the passed
|
||||
// block hash (they are no longer orphans if true) and potentially accepts them.
|
||||
// It repeats the process for the newly accepted blocks (to detect further
|
||||
// orphans which may no longer be orphans) until there are no more.
|
||||
//
|
||||
// The flags do not modify the behavior of this function directly, however they
|
||||
// are needed to pass along to maybeAcceptBlock.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) processOrphans(hash *chainhash.Hash, flags BehaviorFlags) error {
|
||||
// Start with processing at least the passed hash. Leave a little room
|
||||
// for additional orphan blocks that need to be processed without
|
||||
// needing to grow the array in the common case.
|
||||
processHashes := make([]*chainhash.Hash, 0, 10)
|
||||
processHashes = append(processHashes, hash)
|
||||
for len(processHashes) > 0 {
|
||||
// Pop the first hash to process from the slice.
|
||||
processHash := processHashes[0]
|
||||
processHashes[0] = nil // Prevent GC leak.
|
||||
processHashes = processHashes[1:]
|
||||
|
||||
// Look up all orphans that are parented by the block we just
|
||||
// accepted. This will typically only be one, but it could
|
||||
// be multiple if multiple blocks are mined and broadcast
|
||||
// around the same time. The one with the most proof of work
|
||||
// will eventually win out. An indexing for loop is
|
||||
// intentionally used over a range here as range does not
|
||||
// reevaluate the slice on each iteration nor does it adjust the
|
||||
// index for the modified slice.
|
||||
for i := 0; i < len(b.prevOrphans[*processHash]); i++ {
|
||||
orphan := b.prevOrphans[*processHash][i]
|
||||
if orphan == nil {
|
||||
log.Warnf("Found a nil entry at index %d in the "+
|
||||
"orphan dependency list for block %v", i,
|
||||
processHash)
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove the orphan from the orphan pool.
|
||||
orphanHash := orphan.block.Hash()
|
||||
b.removeOrphanBlock(orphan)
|
||||
i--
|
||||
|
||||
// Potentially accept the block into the block chain.
|
||||
_, err := b.maybeAcceptBlock(orphan.block, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add this block to the list of blocks to process so
|
||||
// any orphan blocks that depend on this block are
|
||||
// handled too.
|
||||
processHashes = append(processHashes, orphanHash)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProcessBlock is the main workhorse for handling insertion of new blocks into
|
||||
// the block chain. It includes functionality such as rejecting duplicate
|
||||
// blocks, ensuring blocks follow all rules, orphan handling, and insertion into
|
||||
// the block chain along with best chain selection and reorganization.
|
||||
//
|
||||
// When no errors occurred during processing, the first return value indicates
|
||||
// whether or not the block is on the main chain and the second indicates
|
||||
// whether or not the block is an orphan.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) ProcessBlock(block *btcutil.Block, flags BehaviorFlags) (bool, bool, error) {
|
||||
b.chainLock.Lock()
|
||||
defer b.chainLock.Unlock()
|
||||
|
||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||
|
||||
blockHash := block.Hash()
|
||||
log.Tracef("Processing block %v", blockHash)
|
||||
|
||||
// The block must not already exist in the main chain or side chains.
|
||||
exists, err := b.blockExists(blockHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if exists {
|
||||
str := fmt.Sprintf("already have block %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
|
||||
// The block must not already exist as an orphan.
|
||||
if _, exists := b.orphans[*blockHash]; exists {
|
||||
str := fmt.Sprintf("already have block (orphan) %v", blockHash)
|
||||
return false, false, ruleError(ErrDuplicateBlock, str)
|
||||
}
|
||||
|
||||
// Perform preliminary sanity checks on the block and its transactions.
|
||||
err = checkBlockSanity(block, b.chainParams.PowLimit, b.timeSource, flags)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
// Find the previous checkpoint and perform some additional checks based
|
||||
// on the checkpoint. This provides a few nice properties such as
|
||||
// preventing old side chain blocks before the last checkpoint,
|
||||
// rejecting easy to mine, but otherwise bogus, blocks that could be
|
||||
// used to eat memory, and ensuring expected (versus claimed) proof of
|
||||
// work requirements since the previous checkpoint are met.
|
||||
blockHeader := &block.MsgBlock().Header
|
||||
checkpointNode, err := b.findPreviousCheckpoint()
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if checkpointNode != nil {
|
||||
// Ensure the block timestamp is after the checkpoint timestamp.
|
||||
checkpointTime := time.Unix(checkpointNode.timestamp, 0)
|
||||
if blockHeader.Timestamp.Before(checkpointTime) {
|
||||
str := fmt.Sprintf("block %v has timestamp %v before "+
|
||||
"last checkpoint timestamp %v", blockHash,
|
||||
blockHeader.Timestamp, checkpointTime)
|
||||
return false, false, ruleError(ErrCheckpointTimeTooOld, str)
|
||||
}
|
||||
if !fastAdd {
|
||||
// Even though the checks prior to now have already ensured the
|
||||
// proof of work exceeds the claimed amount, the claimed amount
|
||||
// is a field in the block header which could be forged. This
|
||||
// check ensures the proof of work is at least the minimum
|
||||
// expected based on elapsed time since the last checkpoint and
|
||||
// maximum adjustment allowed by the retarget rules.
|
||||
duration := blockHeader.Timestamp.Sub(checkpointTime)
|
||||
requiredTarget := CompactToBig(b.calcEasiestDifficulty(
|
||||
checkpointNode.bits, duration))
|
||||
currentTarget := CompactToBig(blockHeader.Bits)
|
||||
if currentTarget.Cmp(requiredTarget) > 0 {
|
||||
str := fmt.Sprintf("block target difficulty of %064x "+
|
||||
"is too low when compared to the previous "+
|
||||
"checkpoint", currentTarget)
|
||||
return false, false, ruleError(ErrDifficultyTooLow, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle orphan blocks.
|
||||
prevHash := &blockHeader.PrevBlock
|
||||
prevHashExists, err := b.blockExists(prevHash)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if !prevHashExists {
|
||||
log.Infof("Adding orphan block %v with parent %v", blockHash, prevHash)
|
||||
b.addOrphanBlock(block)
|
||||
|
||||
return false, true, nil
|
||||
}
|
||||
|
||||
// The block has passed all context independent checks and appears sane
|
||||
// enough to potentially accept it into the block chain.
|
||||
isMainChain, err := b.maybeAcceptBlock(block, flags)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
// Accept any orphan blocks that depend on this block (they are
|
||||
// no longer orphans) and repeat for those accepted blocks until
|
||||
// there are no more.
|
||||
err = b.processOrphans(blockHash, flags)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
log.Debugf("Accepted block %v", blockHash)
|
||||
|
||||
return isMainChain, false, nil
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// txValidateItem holds a transaction along with which input to validate.
|
||||
type txValidateItem struct {
|
||||
txInIndex int
|
||||
txIn *wire.TxIn
|
||||
tx *btcutil.Tx
|
||||
sigHashes *txscript.TxSigHashes
|
||||
}
|
||||
|
||||
// txValidator provides a type which asynchronously validates transaction
|
||||
// inputs. It provides several channels for communication and a processing
|
||||
// function that is intended to be in run multiple goroutines.
|
||||
type txValidator struct {
|
||||
validateChan chan *txValidateItem
|
||||
quitChan chan struct{}
|
||||
resultChan chan error
|
||||
utxoView *UtxoViewpoint
|
||||
flags txscript.ScriptFlags
|
||||
sigCache *txscript.SigCache
|
||||
hashCache *txscript.HashCache
|
||||
}
|
||||
|
||||
// sendResult sends the result of a script pair validation on the internal
|
||||
// result channel while respecting the quit channel. This allows orderly
|
||||
// shutdown when the validation process is aborted early due to a validation
|
||||
// error in one of the other goroutines.
|
||||
func (v *txValidator) sendResult(result error) {
|
||||
select {
|
||||
case v.resultChan <- result:
|
||||
case <-v.quitChan:
|
||||
}
|
||||
}
|
||||
|
||||
// validateHandler consumes items to validate from the internal validate channel
|
||||
// and returns the result of the validation on the internal result channel. It
|
||||
// must be run as a goroutine.
|
||||
func (v *txValidator) validateHandler() {
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case txVI := <-v.validateChan:
|
||||
// Ensure the referenced input utxo is available.
|
||||
txIn := txVI.txIn
|
||||
utxo := v.utxoView.LookupEntry(txIn.PreviousOutPoint)
|
||||
if utxo == nil {
|
||||
str := fmt.Sprintf("unable to find unspent "+
|
||||
"output %v referenced from "+
|
||||
"transaction %s:%d",
|
||||
txIn.PreviousOutPoint, txVI.tx.Hash(),
|
||||
txVI.txInIndex)
|
||||
err := ruleError(ErrMissingTxOut, str)
|
||||
v.sendResult(err)
|
||||
break out
|
||||
}
|
||||
|
||||
// Create a new script engine for the script pair.
|
||||
sigScript := txIn.SignatureScript
|
||||
witness := txIn.Witness
|
||||
pkScript := utxo.PkScript()
|
||||
inputAmount := utxo.Amount()
|
||||
vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(),
|
||||
txVI.txInIndex, v.flags, v.sigCache, txVI.sigHashes,
|
||||
inputAmount)
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("failed to parse input "+
|
||||
"%s:%d which references output %v - "+
|
||||
"%v (input witness %x, input script "+
|
||||
"bytes %x, prev output script bytes %x)",
|
||||
txVI.tx.Hash(), txVI.txInIndex,
|
||||
txIn.PreviousOutPoint, err, witness,
|
||||
sigScript, pkScript)
|
||||
err := ruleError(ErrScriptMalformed, str)
|
||||
v.sendResult(err)
|
||||
break out
|
||||
}
|
||||
|
||||
// Execute the script pair.
|
||||
if err := vm.Execute(); err != nil {
|
||||
str := fmt.Sprintf("failed to validate input "+
|
||||
"%s:%d which references output %v - "+
|
||||
"%v (input witness %x, input script "+
|
||||
"bytes %x, prev output script bytes %x)",
|
||||
txVI.tx.Hash(), txVI.txInIndex,
|
||||
txIn.PreviousOutPoint, err, witness,
|
||||
sigScript, pkScript)
|
||||
err := ruleError(ErrScriptValidation, str)
|
||||
v.sendResult(err)
|
||||
break out
|
||||
}
|
||||
|
||||
// Validation succeeded.
|
||||
v.sendResult(nil)
|
||||
|
||||
case <-v.quitChan:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate validates the scripts for all of the passed transaction inputs using
|
||||
// multiple goroutines.
|
||||
func (v *txValidator) Validate(items []*txValidateItem) error {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Limit the number of goroutines to do script validation based on the
|
||||
// number of processor cores. This helps ensure the system stays
|
||||
// reasonably responsive under heavy load.
|
||||
maxGoRoutines := runtime.NumCPU() * 3
|
||||
if maxGoRoutines <= 0 {
|
||||
maxGoRoutines = 1
|
||||
}
|
||||
if maxGoRoutines > len(items) {
|
||||
maxGoRoutines = len(items)
|
||||
}
|
||||
|
||||
// Start up validation handlers that are used to asynchronously
|
||||
// validate each transaction input.
|
||||
for i := 0; i < maxGoRoutines; i++ {
|
||||
go v.validateHandler()
|
||||
}
|
||||
|
||||
// Validate each of the inputs. The quit channel is closed when any
|
||||
// errors occur so all processing goroutines exit regardless of which
|
||||
// input had the validation error.
|
||||
numInputs := len(items)
|
||||
currentItem := 0
|
||||
processedItems := 0
|
||||
for processedItems < numInputs {
|
||||
// Only send items while there are still items that need to
|
||||
// be processed. The select statement will never select a nil
|
||||
// channel.
|
||||
var validateChan chan *txValidateItem
|
||||
var item *txValidateItem
|
||||
if currentItem < numInputs {
|
||||
validateChan = v.validateChan
|
||||
item = items[currentItem]
|
||||
}
|
||||
|
||||
select {
|
||||
case validateChan <- item:
|
||||
currentItem++
|
||||
|
||||
case err := <-v.resultChan:
|
||||
processedItems++
|
||||
if err != nil {
|
||||
close(v.quitChan)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(v.quitChan)
|
||||
return nil
|
||||
}
|
||||
|
||||
// newTxValidator returns a new instance of txValidator to be used for
|
||||
// validating transaction scripts asynchronously.
|
||||
func newTxValidator(utxoView *UtxoViewpoint, flags txscript.ScriptFlags,
|
||||
sigCache *txscript.SigCache, hashCache *txscript.HashCache) *txValidator {
|
||||
return &txValidator{
|
||||
validateChan: make(chan *txValidateItem),
|
||||
quitChan: make(chan struct{}),
|
||||
resultChan: make(chan error),
|
||||
utxoView: utxoView,
|
||||
sigCache: sigCache,
|
||||
hashCache: hashCache,
|
||||
flags: flags,
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateTransactionScripts validates the scripts for the passed transaction
|
||||
// using multiple goroutines.
|
||||
func ValidateTransactionScripts(tx *btcutil.Tx, utxoView *UtxoViewpoint,
|
||||
flags txscript.ScriptFlags, sigCache *txscript.SigCache,
|
||||
hashCache *txscript.HashCache) error {
|
||||
|
||||
// First determine if segwit is active according to the scriptFlags. If
|
||||
// it isn't then we don't need to interact with the HashCache.
|
||||
segwitActive := flags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
|
||||
|
||||
// If the hashcache doesn't yet has the sighash midstate for this
|
||||
// transaction, then we'll compute them now so we can re-use them
|
||||
// amongst all worker validation goroutines.
|
||||
if segwitActive && tx.MsgTx().HasWitness() &&
|
||||
!hashCache.ContainsHashes(tx.Hash()) {
|
||||
hashCache.AddSigHashes(tx.MsgTx())
|
||||
}
|
||||
|
||||
var cachedHashes *txscript.TxSigHashes
|
||||
if segwitActive && tx.MsgTx().HasWitness() {
|
||||
// The same pointer to the transaction's sighash midstate will
|
||||
// be re-used amongst all validation goroutines. By
|
||||
// pre-computing the sighash here instead of during validation,
|
||||
// we ensure the sighashes
|
||||
// are only computed once.
|
||||
cachedHashes, _ = hashCache.GetSigHashes(tx.Hash())
|
||||
}
|
||||
|
||||
// Collect all of the transaction inputs and required information for
|
||||
// validation.
|
||||
txIns := tx.MsgTx().TxIn
|
||||
txValItems := make([]*txValidateItem, 0, len(txIns))
|
||||
for txInIdx, txIn := range txIns {
|
||||
// Skip coinbases.
|
||||
if txIn.PreviousOutPoint.Index == math.MaxUint32 {
|
||||
continue
|
||||
}
|
||||
|
||||
txVI := &txValidateItem{
|
||||
txInIndex: txInIdx,
|
||||
txIn: txIn,
|
||||
tx: tx,
|
||||
sigHashes: cachedHashes,
|
||||
}
|
||||
txValItems = append(txValItems, txVI)
|
||||
}
|
||||
|
||||
// Validate all of the inputs.
|
||||
validator := newTxValidator(utxoView, flags, sigCache, hashCache)
|
||||
return validator.Validate(txValItems)
|
||||
}
|
||||
|
||||
// checkBlockScripts executes and validates the scripts for all transactions in
|
||||
// the passed block using multiple goroutines.
|
||||
func checkBlockScripts(block *btcutil.Block, utxoView *UtxoViewpoint,
|
||||
scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache,
|
||||
hashCache *txscript.HashCache) error {
|
||||
|
||||
// First determine if segwit is active according to the scriptFlags. If
|
||||
// it isn't then we don't need to interact with the HashCache.
|
||||
segwitActive := scriptFlags&txscript.ScriptVerifyWitness == txscript.ScriptVerifyWitness
|
||||
|
||||
// Collect all of the transaction inputs and required information for
|
||||
// validation for all transactions in the block into a single slice.
|
||||
numInputs := 0
|
||||
for _, tx := range block.Transactions() {
|
||||
numInputs += len(tx.MsgTx().TxIn)
|
||||
}
|
||||
txValItems := make([]*txValidateItem, 0, numInputs)
|
||||
for _, tx := range block.Transactions() {
|
||||
hash := tx.Hash()
|
||||
|
||||
// If the HashCache is present, and it doesn't yet contain the
|
||||
// partial sighashes for this transaction, then we add the
|
||||
// sighashes for the transaction. This allows us to take
|
||||
// advantage of the potential speed savings due to the new
|
||||
// digest algorithm (BIP0143).
|
||||
if segwitActive && tx.HasWitness() && hashCache != nil &&
|
||||
!hashCache.ContainsHashes(hash) {
|
||||
|
||||
hashCache.AddSigHashes(tx.MsgTx())
|
||||
}
|
||||
|
||||
var cachedHashes *txscript.TxSigHashes
|
||||
if segwitActive && tx.HasWitness() {
|
||||
if hashCache != nil {
|
||||
cachedHashes, _ = hashCache.GetSigHashes(hash)
|
||||
} else {
|
||||
cachedHashes = txscript.NewTxSigHashes(tx.MsgTx())
|
||||
}
|
||||
}
|
||||
|
||||
for txInIdx, txIn := range tx.MsgTx().TxIn {
|
||||
// Skip coinbases.
|
||||
if txIn.PreviousOutPoint.Index == math.MaxUint32 {
|
||||
continue
|
||||
}
|
||||
|
||||
txVI := &txValidateItem{
|
||||
txInIndex: txInIdx,
|
||||
txIn: txIn,
|
||||
tx: tx,
|
||||
sigHashes: cachedHashes,
|
||||
}
|
||||
txValItems = append(txValItems, txVI)
|
||||
}
|
||||
}
|
||||
|
||||
// Validate all of the inputs.
|
||||
validator := newTxValidator(utxoView, scriptFlags, sigCache, hashCache)
|
||||
start := time.Now()
|
||||
if err := validator.Validate(txValItems); err != nil {
|
||||
return err
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
log.Tracef("block %v took %v to verify", block.Hash(), elapsed)
|
||||
|
||||
// If the HashCache is present, once we have validated the block, we no
|
||||
// longer need the cached hashes for these transactions, so we purge
|
||||
// them from the cache.
|
||||
if segwitActive && hashCache != nil {
|
||||
for _, tx := range block.Transactions() {
|
||||
if tx.MsgTx().HasWitness() {
|
||||
hashCache.PurgeSigHashes(tx.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
// Copyright (c) 2016-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
)
|
||||
|
||||
// ThresholdState define the various threshold states used when voting on
|
||||
// consensus changes.
|
||||
type ThresholdState byte
|
||||
|
||||
// These constants are used to identify specific threshold states.
|
||||
const (
|
||||
// ThresholdDefined is the first state for each deployment and is the
|
||||
// state for the genesis block has by definition for all deployments.
|
||||
ThresholdDefined ThresholdState = iota
|
||||
|
||||
// ThresholdStarted is the state for a deployment once its start time
|
||||
// has been reached.
|
||||
ThresholdStarted
|
||||
|
||||
// ThresholdLockedIn is the state for a deployment during the retarget
|
||||
// period which is after the ThresholdStarted state period and the
|
||||
// number of blocks that have voted for the deployment equal or exceed
|
||||
// the required number of votes for the deployment.
|
||||
ThresholdLockedIn
|
||||
|
||||
// ThresholdActive is the state for a deployment for all blocks after a
|
||||
// retarget period in which the deployment was in the ThresholdLockedIn
|
||||
// state.
|
||||
ThresholdActive
|
||||
|
||||
// ThresholdFailed is the state for a deployment once its expiration
|
||||
// time has been reached and it did not reach the ThresholdLockedIn
|
||||
// state.
|
||||
ThresholdFailed
|
||||
|
||||
// numThresholdsStates is the maximum number of threshold states used in
|
||||
// tests.
|
||||
numThresholdsStates
|
||||
)
|
||||
|
||||
// thresholdStateStrings is a map of ThresholdState values back to their
|
||||
// constant names for pretty printing.
|
||||
var thresholdStateStrings = map[ThresholdState]string{
|
||||
ThresholdDefined: "ThresholdDefined",
|
||||
ThresholdStarted: "ThresholdStarted",
|
||||
ThresholdLockedIn: "ThresholdLockedIn",
|
||||
ThresholdActive: "ThresholdActive",
|
||||
ThresholdFailed: "ThresholdFailed",
|
||||
}
|
||||
|
||||
// String returns the ThresholdState as a human-readable name.
|
||||
func (t ThresholdState) String() string {
|
||||
if s := thresholdStateStrings[t]; s != "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown ThresholdState (%d)", int(t))
|
||||
}
|
||||
|
||||
// thresholdConditionChecker provides a generic interface that is invoked to
|
||||
// determine when a consensus rule change threshold should be changed.
|
||||
type thresholdConditionChecker interface {
|
||||
// BeginTime returns the unix timestamp for the median block time after
|
||||
// which voting on a rule change starts (at the next window).
|
||||
BeginTime() uint64
|
||||
|
||||
// EndTime returns the unix timestamp for the median block time after
|
||||
// which an attempted rule change fails if it has not already been
|
||||
// locked in or activated.
|
||||
EndTime() uint64
|
||||
|
||||
// RuleChangeActivationThreshold is the number of blocks for which the
|
||||
// condition must be true in order to lock in a rule change.
|
||||
RuleChangeActivationThreshold() uint32
|
||||
|
||||
// MinerConfirmationWindow is the number of blocks in each threshold
|
||||
// state retarget window.
|
||||
MinerConfirmationWindow() uint32
|
||||
|
||||
// Condition returns whether or not the rule change activation condition
|
||||
// has been met. This typically involves checking whether or not the
|
||||
// bit associated with the condition is set, but can be more complex as
|
||||
// needed.
|
||||
Condition(*blockNode) (bool, error)
|
||||
}
|
||||
|
||||
// thresholdStateCache provides a type to cache the threshold states of each
|
||||
// threshold window for a set of IDs.
|
||||
type thresholdStateCache struct {
|
||||
entries map[chainhash.Hash]ThresholdState
|
||||
}
|
||||
|
||||
// Lookup returns the threshold state associated with the given hash along with
|
||||
// a boolean that indicates whether or not it is valid.
|
||||
func (c *thresholdStateCache) Lookup(hash *chainhash.Hash) (ThresholdState, bool) {
|
||||
state, ok := c.entries[*hash]
|
||||
return state, ok
|
||||
}
|
||||
|
||||
// Update updates the cache to contain the provided hash to threshold state
|
||||
// mapping.
|
||||
func (c *thresholdStateCache) Update(hash *chainhash.Hash, state ThresholdState) {
|
||||
c.entries[*hash] = state
|
||||
}
|
||||
|
||||
// newThresholdCaches returns a new array of caches to be used when calculating
|
||||
// threshold states.
|
||||
func newThresholdCaches(numCaches uint32) []thresholdStateCache {
|
||||
caches := make([]thresholdStateCache, numCaches)
|
||||
for i := 0; i < len(caches); i++ {
|
||||
caches[i] = thresholdStateCache{
|
||||
entries: make(map[chainhash.Hash]ThresholdState),
|
||||
}
|
||||
}
|
||||
return caches
|
||||
}
|
||||
|
||||
// thresholdState returns the current rule change threshold state for the block
|
||||
// AFTER the given node and deployment ID. The cache is used to ensure the
|
||||
// threshold states for previous windows are only calculated once.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
|
||||
// The threshold state for the window that contains the genesis block is
|
||||
// defined by definition.
|
||||
confirmationWindow := int32(checker.MinerConfirmationWindow())
|
||||
if prevNode == nil || (prevNode.height+1) < confirmationWindow {
|
||||
return ThresholdDefined, nil
|
||||
}
|
||||
|
||||
// Get the ancestor that is the last block of the previous confirmation
|
||||
// window in order to get its threshold state. This can be done because
|
||||
// the state is the same for all blocks within a given window.
|
||||
prevNode = prevNode.Ancestor(prevNode.height -
|
||||
(prevNode.height+1)%confirmationWindow)
|
||||
|
||||
// Iterate backwards through each of the previous confirmation windows
|
||||
// to find the most recently cached threshold state.
|
||||
var neededStates []*blockNode
|
||||
for prevNode != nil {
|
||||
// Nothing more to do if the state of the block is already
|
||||
// cached.
|
||||
if _, ok := cache.Lookup(&prevNode.hash); ok {
|
||||
break
|
||||
}
|
||||
|
||||
// The start and expiration times are based on the median block
|
||||
// time, so calculate it now.
|
||||
medianTime := prevNode.CalcPastMedianTime()
|
||||
|
||||
// The state is simply defined if the start time hasn't been
|
||||
// been reached yet.
|
||||
if uint64(medianTime.Unix()) < checker.BeginTime() {
|
||||
cache.Update(&prevNode.hash, ThresholdDefined)
|
||||
break
|
||||
}
|
||||
|
||||
// Add this node to the list of nodes that need the state
|
||||
// calculated and cached.
|
||||
neededStates = append(neededStates, prevNode)
|
||||
|
||||
// Get the ancestor that is the last block of the previous
|
||||
// confirmation window.
|
||||
prevNode = prevNode.RelativeAncestor(confirmationWindow)
|
||||
}
|
||||
|
||||
// Start with the threshold state for the most recent confirmation
|
||||
// window that has a cached state.
|
||||
state := ThresholdDefined
|
||||
if prevNode != nil {
|
||||
var ok bool
|
||||
state, ok = cache.Lookup(&prevNode.hash)
|
||||
if !ok {
|
||||
return ThresholdFailed, AssertError(fmt.Sprintf(
|
||||
"thresholdState: cache lookup failed for %v",
|
||||
prevNode.hash))
|
||||
}
|
||||
}
|
||||
|
||||
// Since each threshold state depends on the state of the previous
|
||||
// window, iterate starting from the oldest unknown window.
|
||||
for neededNum := len(neededStates) - 1; neededNum >= 0; neededNum-- {
|
||||
prevNode := neededStates[neededNum]
|
||||
|
||||
switch state {
|
||||
case ThresholdDefined:
|
||||
// The deployment of the rule change fails if it expires
|
||||
// before it is accepted and locked in.
|
||||
medianTime := prevNode.CalcPastMedianTime()
|
||||
medianTimeUnix := uint64(medianTime.Unix())
|
||||
if medianTimeUnix >= checker.EndTime() {
|
||||
state = ThresholdFailed
|
||||
break
|
||||
}
|
||||
|
||||
// The state for the rule moves to the started state
|
||||
// once its start time has been reached (and it hasn't
|
||||
// already expired per the above).
|
||||
if medianTimeUnix >= checker.BeginTime() {
|
||||
state = ThresholdStarted
|
||||
}
|
||||
|
||||
case ThresholdStarted:
|
||||
// The deployment of the rule change fails if it expires
|
||||
// before it is accepted and locked in.
|
||||
medianTime := prevNode.CalcPastMedianTime()
|
||||
if uint64(medianTime.Unix()) >= checker.EndTime() {
|
||||
state = ThresholdFailed
|
||||
break
|
||||
}
|
||||
|
||||
// At this point, the rule change is still being voted
|
||||
// on by the miners, so iterate backwards through the
|
||||
// confirmation window to count all of the votes in it.
|
||||
var count uint32
|
||||
countNode := prevNode
|
||||
for i := int32(0); i < confirmationWindow; i++ {
|
||||
condition, err := checker.Condition(countNode)
|
||||
if err != nil {
|
||||
return ThresholdFailed, err
|
||||
}
|
||||
if condition {
|
||||
count++
|
||||
}
|
||||
|
||||
// Get the previous block node.
|
||||
countNode = countNode.parent
|
||||
}
|
||||
|
||||
// The state is locked in if the number of blocks in the
|
||||
// period that voted for the rule change meets the
|
||||
// activation threshold.
|
||||
if count >= checker.RuleChangeActivationThreshold() {
|
||||
state = ThresholdLockedIn
|
||||
}
|
||||
|
||||
case ThresholdLockedIn:
|
||||
// The new rule becomes active when its previous state
|
||||
// was locked in.
|
||||
state = ThresholdActive
|
||||
|
||||
// Nothing to do if the previous state is active or failed since
|
||||
// they are both terminal states.
|
||||
case ThresholdActive:
|
||||
case ThresholdFailed:
|
||||
}
|
||||
|
||||
// Update the cache to avoid recalculating the state in the
|
||||
// future.
|
||||
cache.Update(&prevNode.hash, state)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// ThresholdState returns the current rule change threshold state of the given
|
||||
// deployment ID for the block AFTER the end of the current best chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
|
||||
b.chainLock.Lock()
|
||||
state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
|
||||
b.chainLock.Unlock()
|
||||
|
||||
return state, err
|
||||
}
|
||||
|
||||
// IsDeploymentActive returns true if the target deploymentID is active, and
|
||||
// false otherwise.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) IsDeploymentActive(deploymentID uint32) (bool, error) {
|
||||
b.chainLock.Lock()
|
||||
state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
|
||||
b.chainLock.Unlock()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return state == ThresholdActive, nil
|
||||
}
|
||||
|
||||
// deploymentState returns the current rule change threshold for a given
|
||||
// deploymentID. The threshold is evaluated from the point of view of the block
|
||||
// node passed in as the first argument to this method.
|
||||
//
|
||||
// It is important to note that, as the variable name indicates, this function
|
||||
// expects the block node prior to the block for which the deployment state is
|
||||
// desired. In other words, the returned deployment state is for the block
|
||||
// AFTER the passed node.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) deploymentState(prevNode *blockNode, deploymentID uint32) (ThresholdState, error) {
|
||||
if deploymentID > uint32(len(b.chainParams.Deployments)) {
|
||||
return ThresholdFailed, DeploymentError(deploymentID)
|
||||
}
|
||||
|
||||
deployment := &b.chainParams.Deployments[deploymentID]
|
||||
checker := deploymentChecker{deployment: deployment, chain: b}
|
||||
cache := &b.deploymentCaches[deploymentID]
|
||||
|
||||
return b.thresholdState(prevNode, checker, cache)
|
||||
}
|
||||
|
||||
// initThresholdCaches initializes the threshold state caches for each warning
|
||||
// bit and defined deployment and provides warnings if the chain is current per
|
||||
// the warnUnknownVersions and warnUnknownRuleActivations functions.
|
||||
func (b *BlockChain) initThresholdCaches() error {
|
||||
// Initialize the warning and deployment caches by calculating the
|
||||
// threshold state for each of them. This will ensure the caches are
|
||||
// populated and any states that needed to be recalculated due to
|
||||
// definition changes is done now.
|
||||
prevNode := b.bestChain.Tip().parent
|
||||
for bit := uint32(0); bit < vbNumBits; bit++ {
|
||||
checker := bitConditionChecker{bit: bit, chain: b}
|
||||
cache := &b.warningCaches[bit]
|
||||
_, err := b.thresholdState(prevNode, checker, cache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for id := 0; id < len(b.chainParams.Deployments); id++ {
|
||||
deployment := &b.chainParams.Deployments[id]
|
||||
cache := &b.deploymentCaches[id]
|
||||
checker := deploymentChecker{deployment: deployment, chain: b}
|
||||
_, err := b.thresholdState(prevNode, checker, cache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// No warnings about unknown rules or versions until the chain is
|
||||
// current.
|
||||
if b.isCurrent() {
|
||||
// Warn if a high enough percentage of the last blocks have
|
||||
// unexpected versions.
|
||||
bestNode := b.bestChain.Tip()
|
||||
if err := b.warnUnknownVersions(bestNode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Warn if any unknown new rules are either about to activate or
|
||||
// have already been activated.
|
||||
if err := b.warnUnknownRuleActivations(bestNode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
// timeSorter implements sort.Interface to allow a slice of timestamps to
|
||||
// be sorted.
|
||||
type timeSorter []int64
|
||||
|
||||
// Len returns the number of timestamps in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s timeSorter) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// Swap swaps the timestamps at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s timeSorter) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// Less returns whether the timstamp with index i should sort before the
|
||||
// timestamp with index j. It is part of the sort.Interface implementation.
|
||||
func (s timeSorter) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
}
|
|
@ -0,0 +1,604 @@
|
|||
// Copyright (c) 2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
// blockHdrOffset defines the offsets into a v1 block index row for the
|
||||
// block header.
|
||||
//
|
||||
// The serialized block index row format is:
|
||||
// <blocklocation><blockheader>
|
||||
blockHdrOffset = 12
|
||||
)
|
||||
|
||||
// errInterruptRequested indicates that an operation was cancelled due
|
||||
// to a user-requested interrupt.
|
||||
var errInterruptRequested = errors.New("interrupt requested")
|
||||
|
||||
// interruptRequested returns true when the provided channel has been closed.
|
||||
// This simplifies early shutdown slightly since the caller can just use an if
|
||||
// statement instead of a select.
|
||||
func interruptRequested(interrupted <-chan struct{}) bool {
|
||||
select {
|
||||
case <-interrupted:
|
||||
return true
|
||||
default:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// blockChainContext represents a particular block's placement in the block
|
||||
// chain. This is used by the block index migration to track block metadata that
|
||||
// will be written to disk.
|
||||
type blockChainContext struct {
|
||||
parent *chainhash.Hash
|
||||
children []*chainhash.Hash
|
||||
height int32
|
||||
mainChain bool
|
||||
}
|
||||
|
||||
// migrateBlockIndex migrates all block entries from the v1 block index bucket
|
||||
// to the v2 bucket. The v1 bucket stores all block entries keyed by block hash,
|
||||
// whereas the v2 bucket stores the exact same values, but keyed instead by
|
||||
// block height + hash.
|
||||
func migrateBlockIndex(db database.DB) error {
|
||||
// Hardcoded bucket names so updates to the global values do not affect
|
||||
// old upgrades.
|
||||
v1BucketName := []byte("ffldb-blockidx")
|
||||
v2BucketName := []byte("blockheaderidx")
|
||||
|
||||
err := db.Update(func(dbTx database.Tx) error {
|
||||
v1BlockIdxBucket := dbTx.Metadata().Bucket(v1BucketName)
|
||||
if v1BlockIdxBucket == nil {
|
||||
return fmt.Errorf("Bucket %s does not exist", v1BucketName)
|
||||
}
|
||||
|
||||
log.Info("Re-indexing block information in the database. This might take a while...")
|
||||
|
||||
v2BlockIdxBucket, err :=
|
||||
dbTx.Metadata().CreateBucketIfNotExists(v2BucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get tip of the main chain.
|
||||
serializedData := dbTx.Metadata().Get(chainStateKeyName)
|
||||
state, err := deserializeBestChainState(serializedData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tip := &state.hash
|
||||
|
||||
// Scan the old block index bucket and construct a mapping of each block
|
||||
// to parent block and all child blocks.
|
||||
blocksMap, err := readBlockTree(v1BlockIdxBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use the block graph to calculate the height of each block.
|
||||
err = determineBlockHeights(blocksMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find blocks on the main chain with the block graph and current tip.
|
||||
determineMainChainBlocks(blocksMap, tip)
|
||||
|
||||
// Now that we have heights for all blocks, scan the old block index
|
||||
// bucket and insert all rows into the new one.
|
||||
return v1BlockIdxBucket.ForEach(func(hashBytes, blockRow []byte) error {
|
||||
endOffset := blockHdrOffset + blockHdrSize
|
||||
headerBytes := blockRow[blockHdrOffset:endOffset:endOffset]
|
||||
|
||||
var hash chainhash.Hash
|
||||
copy(hash[:], hashBytes[0:chainhash.HashSize])
|
||||
chainContext := blocksMap[hash]
|
||||
|
||||
if chainContext.height == -1 {
|
||||
return fmt.Errorf("Unable to calculate chain height for "+
|
||||
"stored block %s", hash)
|
||||
}
|
||||
|
||||
// Mark blocks as valid if they are part of the main chain.
|
||||
status := statusDataStored
|
||||
if chainContext.mainChain {
|
||||
status |= statusValid
|
||||
}
|
||||
|
||||
// Write header to v2 bucket
|
||||
value := make([]byte, blockHdrSize+1)
|
||||
copy(value[0:blockHdrSize], headerBytes)
|
||||
value[blockHdrSize] = byte(status)
|
||||
|
||||
key := blockIndexKey(&hash, uint32(chainContext.height))
|
||||
err := v2BlockIdxBucket.Put(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete header from v1 bucket
|
||||
truncatedRow := blockRow[0:blockHdrOffset:blockHdrOffset]
|
||||
return v1BlockIdxBucket.Put(hashBytes, truncatedRow)
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Block database migration complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
// readBlockTree reads the old block index bucket and constructs a mapping of
|
||||
// each block to its parent block and all child blocks. This mapping represents
|
||||
// the full tree of blocks. This function does not populate the height or
|
||||
// mainChain fields of the returned blockChainContext values.
|
||||
func readBlockTree(v1BlockIdxBucket database.Bucket) (map[chainhash.Hash]*blockChainContext, error) {
|
||||
blocksMap := make(map[chainhash.Hash]*blockChainContext)
|
||||
err := v1BlockIdxBucket.ForEach(func(_, blockRow []byte) error {
|
||||
var header wire.BlockHeader
|
||||
endOffset := blockHdrOffset + blockHdrSize
|
||||
headerBytes := blockRow[blockHdrOffset:endOffset:endOffset]
|
||||
err := header.Deserialize(bytes.NewReader(headerBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blockHash := header.BlockHash()
|
||||
prevHash := header.PrevBlock
|
||||
|
||||
if blocksMap[blockHash] == nil {
|
||||
blocksMap[blockHash] = &blockChainContext{height: -1}
|
||||
}
|
||||
if blocksMap[prevHash] == nil {
|
||||
blocksMap[prevHash] = &blockChainContext{height: -1}
|
||||
}
|
||||
|
||||
blocksMap[blockHash].parent = &prevHash
|
||||
blocksMap[prevHash].children =
|
||||
append(blocksMap[prevHash].children, &blockHash)
|
||||
return nil
|
||||
})
|
||||
return blocksMap, err
|
||||
}
|
||||
|
||||
// determineBlockHeights takes a map of block hashes to a slice of child hashes
|
||||
// and uses it to compute the height for each block. The function assigns a
|
||||
// height of 0 to the genesis hash and explores the tree of blocks
|
||||
// breadth-first, assigning a height to every block with a path back to the
|
||||
// genesis block. This function modifies the height field on the blocksMap
|
||||
// entries.
|
||||
func determineBlockHeights(blocksMap map[chainhash.Hash]*blockChainContext) error {
|
||||
queue := list.New()
|
||||
|
||||
// The genesis block is included in blocksMap as a child of the zero hash
|
||||
// because that is the value of the PrevBlock field in the genesis header.
|
||||
preGenesisContext, exists := blocksMap[zeroHash]
|
||||
if !exists || len(preGenesisContext.children) == 0 {
|
||||
return fmt.Errorf("Unable to find genesis block")
|
||||
}
|
||||
|
||||
for _, genesisHash := range preGenesisContext.children {
|
||||
blocksMap[*genesisHash].height = 0
|
||||
queue.PushBack(genesisHash)
|
||||
}
|
||||
|
||||
for e := queue.Front(); e != nil; e = queue.Front() {
|
||||
queue.Remove(e)
|
||||
hash := e.Value.(*chainhash.Hash)
|
||||
height := blocksMap[*hash].height
|
||||
|
||||
// For each block with this one as a parent, assign it a height and
|
||||
// push to queue for future processing.
|
||||
for _, childHash := range blocksMap[*hash].children {
|
||||
blocksMap[*childHash].height = height + 1
|
||||
queue.PushBack(childHash)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// determineMainChainBlocks traverses the block graph down from the tip to
|
||||
// determine which block hashes that are part of the main chain. This function
|
||||
// modifies the mainChain field on the blocksMap entries.
|
||||
func determineMainChainBlocks(blocksMap map[chainhash.Hash]*blockChainContext, tip *chainhash.Hash) {
|
||||
for nextHash := tip; *nextHash != zeroHash; nextHash = blocksMap[*nextHash].parent {
|
||||
blocksMap[*nextHash].mainChain = true
|
||||
}
|
||||
}
|
||||
|
||||
// deserializeUtxoEntryV0 decodes a utxo entry from the passed serialized byte
|
||||
// slice according to the legacy version 0 format into a map of utxos keyed by
|
||||
// the output index within the transaction. The map is necessary because the
|
||||
// previous format encoded all unspent outputs for a transaction using a single
|
||||
// entry, whereas the new format encodes each unspent output individually.
|
||||
//
|
||||
// The legacy format is as follows:
|
||||
//
|
||||
// <version><height><header code><unspentness bitmap>[<compressed txouts>,...]
|
||||
//
|
||||
// Field Type Size
|
||||
// version VLQ variable
|
||||
// block height VLQ variable
|
||||
// header code VLQ variable
|
||||
// unspentness bitmap []byte variable
|
||||
// compressed txouts
|
||||
// compressed amount VLQ variable
|
||||
// compressed script []byte variable
|
||||
//
|
||||
// The serialized header code format is:
|
||||
// bit 0 - containing transaction is a coinbase
|
||||
// bit 1 - output zero is unspent
|
||||
// bit 2 - output one is unspent
|
||||
// bits 3-x - number of bytes in unspentness bitmap. When both bits 1 and 2
|
||||
// are unset, it encodes N-1 since there must be at least one unspent
|
||||
// output.
|
||||
//
|
||||
// The rationale for the header code scheme is as follows:
|
||||
// - Transactions which only pay to a single output and a change output are
|
||||
// extremely common, thus an extra byte for the unspentness bitmap can be
|
||||
// avoided for them by encoding those two outputs in the low order bits.
|
||||
// - Given it is encoded as a VLQ which can encode values up to 127 with a
|
||||
// single byte, that leaves 4 bits to represent the number of bytes in the
|
||||
// unspentness bitmap while still only consuming a single byte for the
|
||||
// header code. In other words, an unspentness bitmap with up to 120
|
||||
// transaction outputs can be encoded with a single-byte header code.
|
||||
// This covers the vast majority of transactions.
|
||||
// - Encoding N-1 bytes when both bits 1 and 2 are unset allows an additional
|
||||
// 8 outpoints to be encoded before causing the header code to require an
|
||||
// additional byte.
|
||||
//
|
||||
// Example 1:
|
||||
// From tx in main blockchain:
|
||||
// Blk 1, 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098
|
||||
//
|
||||
// 010103320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
|
||||
// <><><><------------------------------------------------------------------>
|
||||
// | | \--------\ |
|
||||
// | height | compressed txout 0
|
||||
// version header code
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 1
|
||||
// - header code: 0x03 (coinbase, output zero unspent, 0 bytes of unspentness)
|
||||
// - unspentness: Nothing since it is zero bytes
|
||||
// - compressed txout 0:
|
||||
// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 BTC)
|
||||
// - 0x04: special script type pay-to-pubkey
|
||||
// - 0x96...52: x-coordinate of the pubkey
|
||||
//
|
||||
// Example 2:
|
||||
// From tx in main blockchain:
|
||||
// Blk 113931, 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f
|
||||
//
|
||||
// 0185f90b0a011200e2ccd6ec7c6e2e581349c77e067385fa8236bf8a800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
|
||||
// <><----><><><------------------------------------------><-------------------------------------------->
|
||||
// | | | \-------------------\ | |
|
||||
// version | \--------\ unspentness | compressed txout 2
|
||||
// height header code compressed txout 0
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 113931
|
||||
// - header code: 0x0a (output zero unspent, 1 byte in unspentness bitmap)
|
||||
// - unspentness: [0x01] (bit 0 is set, so output 0+2 = 2 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 0:
|
||||
// - 0x12: VLQ-encoded compressed amount for 20000000 (0.2 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xe2...8a: pubkey hash
|
||||
// - compressed txout 2:
|
||||
// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 BTC)
|
||||
// - 0x00: special script type pay-to-pubkey-hash
|
||||
// - 0xb8...58: pubkey hash
|
||||
//
|
||||
// Example 3:
|
||||
// From tx in main blockchain:
|
||||
// Blk 338156, 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620
|
||||
//
|
||||
// 0193d06c100000108ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
|
||||
// <><----><><----><-------------------------------------------------->
|
||||
// | | | \-----------------\ |
|
||||
// version | \--------\ unspentness |
|
||||
// height header code compressed txout 22
|
||||
//
|
||||
// - version: 1
|
||||
// - height: 338156
|
||||
// - header code: 0x10 (2+1 = 3 bytes in unspentness bitmap)
|
||||
// NOTE: It's +1 since neither bit 1 nor 2 are set, so N-1 is encoded.
|
||||
// - unspentness: [0x00 0x00 0x10] (bit 20 is set, so output 20+2 = 22 is unspent)
|
||||
// NOTE: It's +2 since the first two outputs are encoded in the header code
|
||||
// - compressed txout 22:
|
||||
// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 BTC)
|
||||
// - 0x01: special script type pay-to-script-hash
|
||||
// - 0x1d...e6: script hash
|
||||
func deserializeUtxoEntryV0(serialized []byte) (map[uint32]*UtxoEntry, error) {
|
||||
// Deserialize the version.
|
||||
//
|
||||
// NOTE: Ignore version since it is no longer used in the new format.
|
||||
_, bytesRead := deserializeVLQ(serialized)
|
||||
offset := bytesRead
|
||||
if offset >= len(serialized) {
|
||||
return nil, errDeserialize("unexpected end of data after version")
|
||||
}
|
||||
|
||||
// Deserialize the block height.
|
||||
blockHeight, bytesRead := deserializeVLQ(serialized[offset:])
|
||||
offset += bytesRead
|
||||
if offset >= len(serialized) {
|
||||
return nil, errDeserialize("unexpected end of data after height")
|
||||
}
|
||||
|
||||
// Deserialize the header code.
|
||||
code, bytesRead := deserializeVLQ(serialized[offset:])
|
||||
offset += bytesRead
|
||||
if offset >= len(serialized) {
|
||||
return nil, errDeserialize("unexpected end of data after header")
|
||||
}
|
||||
|
||||
// Decode the header code.
|
||||
//
|
||||
// Bit 0 indicates whether the containing transaction is a coinbase.
|
||||
// Bit 1 indicates output 0 is unspent.
|
||||
// Bit 2 indicates output 1 is unspent.
|
||||
// Bits 3-x encodes the number of non-zero unspentness bitmap bytes that
|
||||
// follow. When both output 0 and 1 are spent, it encodes N-1.
|
||||
isCoinBase := code&0x01 != 0
|
||||
output0Unspent := code&0x02 != 0
|
||||
output1Unspent := code&0x04 != 0
|
||||
numBitmapBytes := code >> 3
|
||||
if !output0Unspent && !output1Unspent {
|
||||
numBitmapBytes++
|
||||
}
|
||||
|
||||
// Ensure there are enough bytes left to deserialize the unspentness
|
||||
// bitmap.
|
||||
if uint64(len(serialized[offset:])) < numBitmapBytes {
|
||||
return nil, errDeserialize("unexpected end of data for " +
|
||||
"unspentness bitmap")
|
||||
}
|
||||
|
||||
// Add sparse output for unspent outputs 0 and 1 as needed based on the
|
||||
// details provided by the header code.
|
||||
var outputIndexes []uint32
|
||||
if output0Unspent {
|
||||
outputIndexes = append(outputIndexes, 0)
|
||||
}
|
||||
if output1Unspent {
|
||||
outputIndexes = append(outputIndexes, 1)
|
||||
}
|
||||
|
||||
// Decode the unspentness bitmap adding a sparse output for each unspent
|
||||
// output.
|
||||
for i := uint32(0); i < uint32(numBitmapBytes); i++ {
|
||||
unspentBits := serialized[offset]
|
||||
for j := uint32(0); j < 8; j++ {
|
||||
if unspentBits&0x01 != 0 {
|
||||
// The first 2 outputs are encoded via the
|
||||
// header code, so adjust the output number
|
||||
// accordingly.
|
||||
outputNum := 2 + i*8 + j
|
||||
outputIndexes = append(outputIndexes, outputNum)
|
||||
}
|
||||
unspentBits >>= 1
|
||||
}
|
||||
offset++
|
||||
}
|
||||
|
||||
// Map to hold all of the converted outputs.
|
||||
entries := make(map[uint32]*UtxoEntry)
|
||||
|
||||
// All entries will need to potentially be marked as a coinbase.
|
||||
var packedFlags txoFlags
|
||||
if isCoinBase {
|
||||
packedFlags |= tfCoinBase
|
||||
}
|
||||
|
||||
// Decode and add all of the utxos.
|
||||
for i, outputIndex := range outputIndexes {
|
||||
// Decode the next utxo.
|
||||
amount, pkScript, bytesRead, err := decodeCompressedTxOut(
|
||||
serialized[offset:])
|
||||
if err != nil {
|
||||
return nil, errDeserialize(fmt.Sprintf("unable to "+
|
||||
"decode utxo at index %d: %v", i, err))
|
||||
}
|
||||
offset += bytesRead
|
||||
|
||||
// Create a new utxo entry with the details deserialized above.
|
||||
entries[outputIndex] = &UtxoEntry{
|
||||
amount: int64(amount),
|
||||
pkScript: pkScript,
|
||||
blockHeight: int32(blockHeight),
|
||||
packedFlags: packedFlags,
|
||||
}
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// upgradeUtxoSetToV2 migrates the utxo set entries from version 1 to 2 in
|
||||
// batches. It is guaranteed to updated if this returns without failure.
|
||||
func upgradeUtxoSetToV2(db database.DB, interrupt <-chan struct{}) error {
|
||||
// Hardcoded bucket names so updates to the global values do not affect
|
||||
// old upgrades.
|
||||
var (
|
||||
v1BucketName = []byte("utxoset")
|
||||
v2BucketName = []byte("utxosetv2")
|
||||
)
|
||||
|
||||
log.Infof("Upgrading utxo set to v2. This will take a while...")
|
||||
start := time.Now()
|
||||
|
||||
// Create the new utxo set bucket as needed.
|
||||
err := db.Update(func(dbTx database.Tx) error {
|
||||
_, err := dbTx.Metadata().CreateBucketIfNotExists(v2BucketName)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// doBatch contains the primary logic for upgrading the utxo set from
|
||||
// version 1 to 2 in batches. This is done because the utxo set can be
|
||||
// huge and thus attempting to migrate in a single database transaction
|
||||
// would result in massive memory usage and could potentially crash on
|
||||
// many systems due to ulimits.
|
||||
//
|
||||
// It returns the number of utxos processed.
|
||||
const maxUtxos = 200000
|
||||
doBatch := func(dbTx database.Tx) (uint32, error) {
|
||||
v1Bucket := dbTx.Metadata().Bucket(v1BucketName)
|
||||
v2Bucket := dbTx.Metadata().Bucket(v2BucketName)
|
||||
v1Cursor := v1Bucket.Cursor()
|
||||
|
||||
// Migrate utxos so long as the max number of utxos for this
|
||||
// batch has not been exceeded.
|
||||
var numUtxos uint32
|
||||
for ok := v1Cursor.First(); ok && numUtxos < maxUtxos; ok =
|
||||
v1Cursor.Next() {
|
||||
|
||||
// Old key was the transaction hash.
|
||||
oldKey := v1Cursor.Key()
|
||||
var txHash chainhash.Hash
|
||||
copy(txHash[:], oldKey)
|
||||
|
||||
// Deserialize the old entry which included all utxos
|
||||
// for the given transaction.
|
||||
utxos, err := deserializeUtxoEntryV0(v1Cursor.Value())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Add an entry for each utxo into the new bucket using
|
||||
// the new format.
|
||||
for txOutIdx, utxo := range utxos {
|
||||
reserialized, err := serializeUtxoEntry(utxo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
key := outpointKey(wire.OutPoint{
|
||||
Hash: txHash,
|
||||
Index: txOutIdx,
|
||||
})
|
||||
err = v2Bucket.Put(*key, reserialized)
|
||||
// NOTE: The key is intentionally not recycled
|
||||
// here since the database interface contract
|
||||
// prohibits modifications. It will be garbage
|
||||
// collected normally when the database is done
|
||||
// with it.
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old entry.
|
||||
err = v1Bucket.Delete(oldKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
numUtxos += uint32(len(utxos))
|
||||
|
||||
if interruptRequested(interrupt) {
|
||||
// No error here so the database transaction
|
||||
// is not cancelled and therefore outstanding
|
||||
// work is written to disk.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return numUtxos, nil
|
||||
}
|
||||
|
||||
// Migrate all entries in batches for the reasons mentioned above.
|
||||
var totalUtxos uint64
|
||||
for {
|
||||
var numUtxos uint32
|
||||
err := db.Update(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
numUtxos, err = doBatch(dbTx)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if interruptRequested(interrupt) {
|
||||
return errInterruptRequested
|
||||
}
|
||||
|
||||
if numUtxos == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
totalUtxos += uint64(numUtxos)
|
||||
log.Infof("Migrated %d utxos (%d total)", numUtxos, totalUtxos)
|
||||
}
|
||||
|
||||
// Remove the old bucket and update the utxo set version once it has
|
||||
// been fully migrated.
|
||||
err = db.Update(func(dbTx database.Tx) error {
|
||||
err := dbTx.Metadata().DeleteBucket(v1BucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dbPutVersion(dbTx, utxoSetVersionKeyName, 2)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
seconds := int64(time.Since(start) / time.Second)
|
||||
log.Infof("Done upgrading utxo set. Total utxos: %d in %d seconds",
|
||||
totalUtxos, seconds)
|
||||
return nil
|
||||
}
|
||||
|
||||
// maybeUpgradeDbBuckets checks the database version of the buckets used by this
|
||||
// package and performs any needed upgrades to bring them to the latest version.
|
||||
//
|
||||
// All buckets used by this package are guaranteed to be the latest version if
|
||||
// this function returns without error.
|
||||
func (b *BlockChain) maybeUpgradeDbBuckets(interrupt <-chan struct{}) error {
|
||||
// Load or create bucket versions as needed.
|
||||
var utxoSetVersion uint32
|
||||
err := b.db.Update(func(dbTx database.Tx) error {
|
||||
// Load the utxo set version from the database or create it and
|
||||
// initialize it to version 1 if it doesn't exist.
|
||||
var err error
|
||||
utxoSetVersion, err = dbFetchOrCreateVersion(dbTx,
|
||||
utxoSetVersionKeyName, 1)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the utxo set to v2 if needed.
|
||||
if utxoSetVersion < 2 {
|
||||
if err := upgradeUtxoSetToV2(b.db, interrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,642 @@
|
|||
// Copyright (c) 2015-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/database"
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
// txoFlags is a bitmask defining additional information and state for a
|
||||
// transaction output in a utxo view.
|
||||
type txoFlags uint8
|
||||
|
||||
const (
|
||||
// tfCoinBase indicates that a txout was contained in a coinbase tx.
|
||||
tfCoinBase txoFlags = 1 << iota
|
||||
|
||||
// tfSpent indicates that a txout is spent.
|
||||
tfSpent
|
||||
|
||||
// tfModified indicates that a txout has been modified since it was
|
||||
// loaded.
|
||||
tfModified
|
||||
)
|
||||
|
||||
// UtxoEntry houses details about an individual transaction output in a utxo
|
||||
// view such as whether or not it was contained in a coinbase tx, the height of
|
||||
// the block that contains the tx, whether or not it is spent, its public key
|
||||
// script, and how much it pays.
|
||||
type UtxoEntry struct {
|
||||
// NOTE: Additions, deletions, or modifications to the order of the
|
||||
// definitions in this struct should not be changed without considering
|
||||
// how it affects alignment on 64-bit platforms. The current order is
|
||||
// specifically crafted to result in minimal padding. There will be a
|
||||
// lot of these in memory, so a few extra bytes of padding adds up.
|
||||
|
||||
amount int64
|
||||
pkScript []byte // The public key script for the output.
|
||||
blockHeight int32 // Height of block containing tx.
|
||||
|
||||
// packedFlags contains additional info about output such as whether it
|
||||
// is a coinbase, whether it is spent, and whether it has been modified
|
||||
// since it was loaded. This approach is used in order to reduce memory
|
||||
// usage since there will be a lot of these in memory.
|
||||
packedFlags txoFlags
|
||||
}
|
||||
|
||||
// isModified returns whether or not the output has been modified since it was
|
||||
// loaded.
|
||||
func (entry *UtxoEntry) isModified() bool {
|
||||
return entry.packedFlags&tfModified == tfModified
|
||||
}
|
||||
|
||||
// IsCoinBase returns whether or not the output was contained in a coinbase
|
||||
// transaction.
|
||||
func (entry *UtxoEntry) IsCoinBase() bool {
|
||||
return entry.packedFlags&tfCoinBase == tfCoinBase
|
||||
}
|
||||
|
||||
// BlockHeight returns the height of the block containing the output.
|
||||
func (entry *UtxoEntry) BlockHeight() int32 {
|
||||
return entry.blockHeight
|
||||
}
|
||||
|
||||
// IsSpent returns whether or not the output has been spent based upon the
|
||||
// current state of the unspent transaction output view it was obtained from.
|
||||
func (entry *UtxoEntry) IsSpent() bool {
|
||||
return entry.packedFlags&tfSpent == tfSpent
|
||||
}
|
||||
|
||||
// Spend marks the output as spent. Spending an output that is already spent
|
||||
// has no effect.
|
||||
func (entry *UtxoEntry) Spend() {
|
||||
// Nothing to do if the output is already spent.
|
||||
if entry.IsSpent() {
|
||||
return
|
||||
}
|
||||
|
||||
// Mark the output as spent and modified.
|
||||
entry.packedFlags |= tfSpent | tfModified
|
||||
}
|
||||
|
||||
// Amount returns the amount of the output.
|
||||
func (entry *UtxoEntry) Amount() int64 {
|
||||
return entry.amount
|
||||
}
|
||||
|
||||
// PkScript returns the public key script for the output.
|
||||
func (entry *UtxoEntry) PkScript() []byte {
|
||||
return entry.pkScript
|
||||
}
|
||||
|
||||
// Clone returns a shallow copy of the utxo entry.
|
||||
func (entry *UtxoEntry) Clone() *UtxoEntry {
|
||||
if entry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &UtxoEntry{
|
||||
amount: entry.amount,
|
||||
pkScript: entry.pkScript,
|
||||
blockHeight: entry.blockHeight,
|
||||
packedFlags: entry.packedFlags,
|
||||
}
|
||||
}
|
||||
|
||||
// UtxoViewpoint represents a view into the set of unspent transaction outputs
|
||||
// from a specific point of view in the chain. For example, it could be for
|
||||
// the end of the main chain, some point in the history of the main chain, or
|
||||
// down a side chain.
|
||||
//
|
||||
// The unspent outputs are needed by other transactions for things such as
|
||||
// script validation and double spend prevention.
|
||||
type UtxoViewpoint struct {
|
||||
entries map[wire.OutPoint]*UtxoEntry
|
||||
bestHash chainhash.Hash
|
||||
}
|
||||
|
||||
// BestHash returns the hash of the best block in the chain the view currently
|
||||
// respresents.
|
||||
func (view *UtxoViewpoint) BestHash() *chainhash.Hash {
|
||||
return &view.bestHash
|
||||
}
|
||||
|
||||
// SetBestHash sets the hash of the best block in the chain the view currently
|
||||
// respresents.
|
||||
func (view *UtxoViewpoint) SetBestHash(hash *chainhash.Hash) {
|
||||
view.bestHash = *hash
|
||||
}
|
||||
|
||||
// LookupEntry returns information about a given transaction output according to
|
||||
// the current state of the view. It will return nil if the passed output does
|
||||
// not exist in the view or is otherwise not available such as when it has been
|
||||
// disconnected during a reorg.
|
||||
func (view *UtxoViewpoint) LookupEntry(outpoint wire.OutPoint) *UtxoEntry {
|
||||
return view.entries[outpoint]
|
||||
}
|
||||
|
||||
// addTxOut adds the specified output to the view if it is not provably
|
||||
// unspendable. When the view already has an entry for the output, it will be
|
||||
// marked unspent. All fields will be updated for existing entries since it's
|
||||
// possible it has changed during a reorg.
|
||||
func (view *UtxoViewpoint) addTxOut(outpoint wire.OutPoint, txOut *wire.TxOut, isCoinBase bool, blockHeight int32) {
|
||||
// Don't add provably unspendable outputs.
|
||||
if txscript.IsUnspendable(txOut.PkScript) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update existing entries. All fields are updated because it's
|
||||
// possible (although extremely unlikely) that the existing entry is
|
||||
// being replaced by a different transaction with the same hash. This
|
||||
// is allowed so long as the previous transaction is fully spent.
|
||||
entry := view.LookupEntry(outpoint)
|
||||
if entry == nil {
|
||||
entry = new(UtxoEntry)
|
||||
view.entries[outpoint] = entry
|
||||
}
|
||||
|
||||
entry.amount = txOut.Value
|
||||
entry.pkScript = txOut.PkScript
|
||||
entry.blockHeight = blockHeight
|
||||
entry.packedFlags = tfModified
|
||||
if isCoinBase {
|
||||
entry.packedFlags |= tfCoinBase
|
||||
}
|
||||
}
|
||||
|
||||
// AddTxOut adds the specified output of the passed transaction to the view if
|
||||
// it exists and is not provably unspendable. When the view already has an
|
||||
// entry for the output, it will be marked unspent. All fields will be updated
|
||||
// for existing entries since it's possible it has changed during a reorg.
|
||||
func (view *UtxoViewpoint) AddTxOut(tx *btcutil.Tx, txOutIdx uint32, blockHeight int32) {
|
||||
// Can't add an output for an out of bounds index.
|
||||
if txOutIdx >= uint32(len(tx.MsgTx().TxOut)) {
|
||||
return
|
||||
}
|
||||
|
||||
// Update existing entries. All fields are updated because it's
|
||||
// possible (although extremely unlikely) that the existing entry is
|
||||
// being replaced by a different transaction with the same hash. This
|
||||
// is allowed so long as the previous transaction is fully spent.
|
||||
prevOut := wire.OutPoint{Hash: *tx.Hash(), Index: txOutIdx}
|
||||
txOut := tx.MsgTx().TxOut[txOutIdx]
|
||||
view.addTxOut(prevOut, txOut, IsCoinBase(tx), blockHeight)
|
||||
}
|
||||
|
||||
// AddTxOuts adds all outputs in the passed transaction which are not provably
|
||||
// unspendable to the view. When the view already has entries for any of the
|
||||
// outputs, they are simply marked unspent. All fields will be updated for
|
||||
// existing entries since it's possible it has changed during a reorg.
|
||||
func (view *UtxoViewpoint) AddTxOuts(tx *btcutil.Tx, blockHeight int32) {
|
||||
// Loop all of the transaction outputs and add those which are not
|
||||
// provably unspendable.
|
||||
isCoinBase := IsCoinBase(tx)
|
||||
prevOut := wire.OutPoint{Hash: *tx.Hash()}
|
||||
for txOutIdx, txOut := range tx.MsgTx().TxOut {
|
||||
// Update existing entries. All fields are updated because it's
|
||||
// possible (although extremely unlikely) that the existing
|
||||
// entry is being replaced by a different transaction with the
|
||||
// same hash. This is allowed so long as the previous
|
||||
// transaction is fully spent.
|
||||
prevOut.Index = uint32(txOutIdx)
|
||||
view.addTxOut(prevOut, txOut, isCoinBase, blockHeight)
|
||||
}
|
||||
}
|
||||
|
||||
// connectTransaction updates the view by adding all new utxos created by the
|
||||
// passed transaction and marking all utxos that the transactions spend as
|
||||
// spent. In addition, when the 'stxos' argument is not nil, it will be updated
|
||||
// to append an entry for each spent txout. An error will be returned if the
|
||||
// view does not contain the required utxos.
|
||||
func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32, stxos *[]SpentTxOut) error {
|
||||
// Coinbase transactions don't have any inputs to spend.
|
||||
if IsCoinBase(tx) {
|
||||
// Add the transaction's outputs as available utxos.
|
||||
view.AddTxOuts(tx, blockHeight)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Spend the referenced utxos by marking them spent in the view and,
|
||||
// if a slice was provided for the spent txout details, append an entry
|
||||
// to it.
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
// Ensure the referenced utxo exists in the view. This should
|
||||
// never happen unless there is a bug is introduced in the code.
|
||||
entry := view.entries[txIn.PreviousOutPoint]
|
||||
if entry == nil {
|
||||
return AssertError(fmt.Sprintf("view missing input %v",
|
||||
txIn.PreviousOutPoint))
|
||||
}
|
||||
|
||||
// Only create the stxo details if requested.
|
||||
if stxos != nil {
|
||||
// Populate the stxo details using the utxo entry.
|
||||
var stxo = SpentTxOut{
|
||||
Amount: entry.Amount(),
|
||||
PkScript: entry.PkScript(),
|
||||
Height: entry.BlockHeight(),
|
||||
IsCoinBase: entry.IsCoinBase(),
|
||||
}
|
||||
*stxos = append(*stxos, stxo)
|
||||
}
|
||||
|
||||
// Mark the entry as spent. This is not done until after the
|
||||
// relevant details have been accessed since spending it might
|
||||
// clear the fields from memory in the future.
|
||||
entry.Spend()
|
||||
}
|
||||
|
||||
// Add the transaction's outputs as available utxos.
|
||||
view.AddTxOuts(tx, blockHeight)
|
||||
return nil
|
||||
}
|
||||
|
||||
// connectTransactions updates the view by adding all new utxos created by all
|
||||
// of the transactions in the passed block, marking all utxos the transactions
|
||||
// spend as spent, and setting the best hash for the view to the passed block.
|
||||
// In addition, when the 'stxos' argument is not nil, it will be updated to
|
||||
// append an entry for each spent txout.
|
||||
func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]SpentTxOut) error {
|
||||
for _, tx := range block.Transactions() {
|
||||
err := view.connectTransaction(tx, block.Height(), stxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update the best hash for view to include this block since all of its
|
||||
// transactions have been connected.
|
||||
view.SetBestHash(block.Hash())
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchEntryByHash attempts to find any available utxo for the given hash by
|
||||
// searching the entire set of possible outputs for the given hash. It checks
|
||||
// the view first and then falls back to the database if needed.
|
||||
func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *chainhash.Hash) (*UtxoEntry, error) {
|
||||
// First attempt to find a utxo with the provided hash in the view.
|
||||
prevOut := wire.OutPoint{Hash: *hash}
|
||||
for idx := uint32(0); idx < MaxOutputsPerBlock; idx++ {
|
||||
prevOut.Index = idx
|
||||
entry := view.LookupEntry(prevOut)
|
||||
if entry != nil {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check the database since it doesn't exist in the view. This will
|
||||
// often by the case since only specifically referenced utxos are loaded
|
||||
// into the view.
|
||||
var entry *UtxoEntry
|
||||
err := db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
entry, err = dbFetchUtxoEntryByHash(dbTx, hash)
|
||||
return err
|
||||
})
|
||||
return entry, err
|
||||
}
|
||||
|
||||
// disconnectTransactions updates the view by removing all of the transactions
|
||||
// created by the passed block, restoring all utxos the transactions spent by
|
||||
// using the provided spent txo information, and setting the best hash for the
|
||||
// view to the block before the passed block.
|
||||
func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil.Block, stxos []SpentTxOut) error {
|
||||
// Sanity check the correct number of stxos are provided.
|
||||
if len(stxos) != countSpentOutputs(block) {
|
||||
return AssertError("disconnectTransactions called with bad " +
|
||||
"spent transaction out information")
|
||||
}
|
||||
|
||||
// Loop backwards through all transactions so everything is unspent in
|
||||
// reverse order. This is necessary since transactions later in a block
|
||||
// can spend from previous ones.
|
||||
stxoIdx := len(stxos) - 1
|
||||
transactions := block.Transactions()
|
||||
for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- {
|
||||
tx := transactions[txIdx]
|
||||
|
||||
// All entries will need to potentially be marked as a coinbase.
|
||||
var packedFlags txoFlags
|
||||
isCoinBase := txIdx == 0
|
||||
if isCoinBase {
|
||||
packedFlags |= tfCoinBase
|
||||
}
|
||||
|
||||
// Mark all of the spendable outputs originally created by the
|
||||
// transaction as spent. It is instructive to note that while
|
||||
// the outputs aren't actually being spent here, rather they no
|
||||
// longer exist, since a pruned utxo set is used, there is no
|
||||
// practical difference between a utxo that does not exist and
|
||||
// one that has been spent.
|
||||
//
|
||||
// When the utxo does not already exist in the view, add an
|
||||
// entry for it and then mark it spent. This is done because
|
||||
// the code relies on its existence in the view in order to
|
||||
// signal modifications have happened.
|
||||
txHash := tx.Hash()
|
||||
prevOut := wire.OutPoint{Hash: *txHash}
|
||||
for txOutIdx, txOut := range tx.MsgTx().TxOut {
|
||||
if txscript.IsUnspendable(txOut.PkScript) {
|
||||
continue
|
||||
}
|
||||
|
||||
prevOut.Index = uint32(txOutIdx)
|
||||
entry := view.entries[prevOut]
|
||||
if entry == nil {
|
||||
entry = &UtxoEntry{
|
||||
amount: txOut.Value,
|
||||
pkScript: txOut.PkScript,
|
||||
blockHeight: block.Height(),
|
||||
packedFlags: packedFlags,
|
||||
}
|
||||
|
||||
view.entries[prevOut] = entry
|
||||
}
|
||||
|
||||
entry.Spend()
|
||||
}
|
||||
|
||||
// Loop backwards through all of the transaction inputs (except
|
||||
// for the coinbase which has no inputs) and unspend the
|
||||
// referenced txos. This is necessary to match the order of the
|
||||
// spent txout entries.
|
||||
if isCoinBase {
|
||||
continue
|
||||
}
|
||||
for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- {
|
||||
// Ensure the spent txout index is decremented to stay
|
||||
// in sync with the transaction input.
|
||||
stxo := &stxos[stxoIdx]
|
||||
stxoIdx--
|
||||
|
||||
// When there is not already an entry for the referenced
|
||||
// output in the view, it means it was previously spent,
|
||||
// so create a new utxo entry in order to resurrect it.
|
||||
originOut := &tx.MsgTx().TxIn[txInIdx].PreviousOutPoint
|
||||
entry := view.entries[*originOut]
|
||||
if entry == nil {
|
||||
entry = new(UtxoEntry)
|
||||
view.entries[*originOut] = entry
|
||||
}
|
||||
|
||||
// The legacy v1 spend journal format only stored the
|
||||
// coinbase flag and height when the output was the last
|
||||
// unspent output of the transaction. As a result, when
|
||||
// the information is missing, search for it by scanning
|
||||
// all possible outputs of the transaction since it must
|
||||
// be in one of them.
|
||||
//
|
||||
// It should be noted that this is quite inefficient,
|
||||
// but it realistically will almost never run since all
|
||||
// new entries include the information for all outputs
|
||||
// and thus the only way this will be hit is if a long
|
||||
// enough reorg happens such that a block with the old
|
||||
// spend data is being disconnected. The probability of
|
||||
// that in practice is extremely low to begin with and
|
||||
// becomes vanishingly small the more new blocks are
|
||||
// connected. In the case of a fresh database that has
|
||||
// only ever run with the new v2 format, this code path
|
||||
// will never run.
|
||||
if stxo.Height == 0 {
|
||||
utxo, err := view.fetchEntryByHash(db, txHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if utxo == nil {
|
||||
return AssertError(fmt.Sprintf("unable "+
|
||||
"to resurrect legacy stxo %v",
|
||||
*originOut))
|
||||
}
|
||||
|
||||
stxo.Height = utxo.BlockHeight()
|
||||
stxo.IsCoinBase = utxo.IsCoinBase()
|
||||
}
|
||||
|
||||
// Restore the utxo using the stxo data from the spend
|
||||
// journal and mark it as modified.
|
||||
entry.amount = stxo.Amount
|
||||
entry.pkScript = stxo.PkScript
|
||||
entry.blockHeight = stxo.Height
|
||||
entry.packedFlags = tfModified
|
||||
if stxo.IsCoinBase {
|
||||
entry.packedFlags |= tfCoinBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the best hash for view to the previous block since all of the
|
||||
// transactions for the current block have been disconnected.
|
||||
view.SetBestHash(&block.MsgBlock().Header.PrevBlock)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveEntry removes the given transaction output from the current state of
|
||||
// the view. It will have no effect if the passed output does not exist in the
|
||||
// view.
|
||||
func (view *UtxoViewpoint) RemoveEntry(outpoint wire.OutPoint) {
|
||||
delete(view.entries, outpoint)
|
||||
}
|
||||
|
||||
// Entries returns the underlying map that stores of all the utxo entries.
|
||||
func (view *UtxoViewpoint) Entries() map[wire.OutPoint]*UtxoEntry {
|
||||
return view.entries
|
||||
}
|
||||
|
||||
// commit prunes all entries marked modified that are now fully spent and marks
|
||||
// all entries as unmodified.
|
||||
func (view *UtxoViewpoint) commit() {
|
||||
for outpoint, entry := range view.entries {
|
||||
if entry == nil || (entry.isModified() && entry.IsSpent()) {
|
||||
delete(view.entries, outpoint)
|
||||
continue
|
||||
}
|
||||
|
||||
entry.packedFlags ^= tfModified
|
||||
}
|
||||
}
|
||||
|
||||
// fetchUtxosMain fetches unspent transaction output data about the provided
|
||||
// set of outpoints from the point of view of the end of the main chain at the
|
||||
// time of the call.
|
||||
//
|
||||
// Upon completion of this function, the view will contain an entry for each
|
||||
// requested outpoint. Spent outputs, or those which otherwise don't exist,
|
||||
// will result in a nil entry in the view.
|
||||
func (view *UtxoViewpoint) fetchUtxosMain(db database.DB, outpoints map[wire.OutPoint]struct{}) error {
|
||||
// Nothing to do if there are no requested outputs.
|
||||
if len(outpoints) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load the requested set of unspent transaction outputs from the point
|
||||
// of view of the end of the main chain.
|
||||
//
|
||||
// NOTE: Missing entries are not considered an error here and instead
|
||||
// will result in nil entries in the view. This is intentionally done
|
||||
// so other code can use the presence of an entry in the store as a way
|
||||
// to unnecessarily avoid attempting to reload it from the database.
|
||||
return db.View(func(dbTx database.Tx) error {
|
||||
for outpoint := range outpoints {
|
||||
entry, err := dbFetchUtxoEntry(dbTx, outpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
view.entries[outpoint] = entry
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// fetchUtxos loads the unspent transaction outputs for the provided set of
|
||||
// outputs into the view from the database as needed unless they already exist
|
||||
// in the view in which case they are ignored.
|
||||
func (view *UtxoViewpoint) fetchUtxos(db database.DB, outpoints map[wire.OutPoint]struct{}) error {
|
||||
// Nothing to do if there are no requested outputs.
|
||||
if len(outpoints) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter entries that are already in the view.
|
||||
neededSet := make(map[wire.OutPoint]struct{})
|
||||
for outpoint := range outpoints {
|
||||
// Already loaded into the current view.
|
||||
if _, ok := view.entries[outpoint]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
neededSet[outpoint] = struct{}{}
|
||||
}
|
||||
|
||||
// Request the input utxos from the database.
|
||||
return view.fetchUtxosMain(db, neededSet)
|
||||
}
|
||||
|
||||
// fetchInputUtxos loads the unspent transaction outputs for the inputs
|
||||
// referenced by the transactions in the given block into the view from the
|
||||
// database as needed. In particular, referenced entries that are earlier in
|
||||
// the block are added to the view and entries that are already in the view are
|
||||
// not modified.
|
||||
func (view *UtxoViewpoint) fetchInputUtxos(db database.DB, block *btcutil.Block) error {
|
||||
// Build a map of in-flight transactions because some of the inputs in
|
||||
// this block could be referencing other transactions earlier in this
|
||||
// block which are not yet in the chain.
|
||||
txInFlight := map[chainhash.Hash]int{}
|
||||
transactions := block.Transactions()
|
||||
for i, tx := range transactions {
|
||||
txInFlight[*tx.Hash()] = i
|
||||
}
|
||||
|
||||
// Loop through all of the transaction inputs (except for the coinbase
|
||||
// which has no inputs) collecting them into sets of what is needed and
|
||||
// what is already known (in-flight).
|
||||
neededSet := make(map[wire.OutPoint]struct{})
|
||||
for i, tx := range transactions[1:] {
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
// It is acceptable for a transaction input to reference
|
||||
// the output of another transaction in this block only
|
||||
// if the referenced transaction comes before the
|
||||
// current one in this block. Add the outputs of the
|
||||
// referenced transaction as available utxos when this
|
||||
// is the case. Otherwise, the utxo details are still
|
||||
// needed.
|
||||
//
|
||||
// NOTE: The >= is correct here because i is one less
|
||||
// than the actual position of the transaction within
|
||||
// the block due to skipping the coinbase.
|
||||
originHash := &txIn.PreviousOutPoint.Hash
|
||||
if inFlightIndex, ok := txInFlight[*originHash]; ok &&
|
||||
i >= inFlightIndex {
|
||||
|
||||
originTx := transactions[inFlightIndex]
|
||||
view.AddTxOuts(originTx, block.Height())
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't request entries that are already in the view
|
||||
// from the database.
|
||||
if _, ok := view.entries[txIn.PreviousOutPoint]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
neededSet[txIn.PreviousOutPoint] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Request the input utxos from the database.
|
||||
return view.fetchUtxosMain(db, neededSet)
|
||||
}
|
||||
|
||||
// NewUtxoViewpoint returns a new empty unspent transaction output view.
|
||||
func NewUtxoViewpoint() *UtxoViewpoint {
|
||||
return &UtxoViewpoint{
|
||||
entries: make(map[wire.OutPoint]*UtxoEntry),
|
||||
}
|
||||
}
|
||||
|
||||
// FetchUtxoView loads unspent transaction outputs for the inputs referenced by
|
||||
// the passed transaction from the point of view of the end of the main chain.
|
||||
// It also attempts to fetch the utxos for the outputs of the transaction itself
|
||||
// so the returned view can be examined for duplicate transactions.
|
||||
//
|
||||
// This function is safe for concurrent access however the returned view is NOT.
|
||||
func (b *BlockChain) FetchUtxoView(tx *btcutil.Tx) (*UtxoViewpoint, error) {
|
||||
// Create a set of needed outputs based on those referenced by the
|
||||
// inputs of the passed transaction and the outputs of the transaction
|
||||
// itself.
|
||||
neededSet := make(map[wire.OutPoint]struct{})
|
||||
prevOut := wire.OutPoint{Hash: *tx.Hash()}
|
||||
for txOutIdx := range tx.MsgTx().TxOut {
|
||||
prevOut.Index = uint32(txOutIdx)
|
||||
neededSet[prevOut] = struct{}{}
|
||||
}
|
||||
if !IsCoinBase(tx) {
|
||||
for _, txIn := range tx.MsgTx().TxIn {
|
||||
neededSet[txIn.PreviousOutPoint] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Request the utxos from the point of view of the end of the main
|
||||
// chain.
|
||||
view := NewUtxoViewpoint()
|
||||
b.chainLock.RLock()
|
||||
err := view.fetchUtxosMain(b.db, neededSet)
|
||||
b.chainLock.RUnlock()
|
||||
return view, err
|
||||
}
|
||||
|
||||
// FetchUtxoEntry loads and returns the requested unspent transaction output
|
||||
// from the point of view of the end of the main chain.
|
||||
//
|
||||
// NOTE: Requesting an output for which there is no data will NOT return an
|
||||
// error. Instead both the entry and the error will be nil. This is done to
|
||||
// allow pruning of spent transaction outputs. In practice this means the
|
||||
// caller must check if the returned entry is nil before invoking methods on it.
|
||||
//
|
||||
// This function is safe for concurrent access however the returned entry (if
|
||||
// any) is NOT.
|
||||
func (b *BlockChain) FetchUtxoEntry(outpoint wire.OutPoint) (*UtxoEntry, error) {
|
||||
b.chainLock.RLock()
|
||||
defer b.chainLock.RUnlock()
|
||||
|
||||
var entry *UtxoEntry
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
entry, err = dbFetchUtxoEntry(dbTx, outpoint)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,301 @@
|
|||
// Copyright (c) 2016-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
const (
|
||||
// vbLegacyBlockVersion is the highest legacy block version before the
|
||||
// version bits scheme became active.
|
||||
vbLegacyBlockVersion = 4
|
||||
|
||||
// vbTopBits defines the bits to set in the version to signal that the
|
||||
// version bits scheme is being used.
|
||||
vbTopBits = 0x20000000
|
||||
|
||||
// vbTopMask is the bitmask to use to determine whether or not the
|
||||
// version bits scheme is in use.
|
||||
vbTopMask = 0xe0000000
|
||||
|
||||
// vbNumBits is the total number of bits available for use with the
|
||||
// version bits scheme.
|
||||
vbNumBits = 29
|
||||
|
||||
// unknownVerNumToCheck is the number of previous blocks to consider
|
||||
// when checking for a threshold of unknown block versions for the
|
||||
// purposes of warning the user.
|
||||
unknownVerNumToCheck = 100
|
||||
|
||||
// unknownVerWarnNum is the threshold of previous blocks that have an
|
||||
// unknown version to use for the purposes of warning the user.
|
||||
unknownVerWarnNum = unknownVerNumToCheck / 2
|
||||
)
|
||||
|
||||
// bitConditionChecker provides a thresholdConditionChecker which can be used to
|
||||
// test whether or not a specific bit is set when it's not supposed to be
|
||||
// according to the expected version based on the known deployments and the
|
||||
// current state of the chain. This is useful for detecting and warning about
|
||||
// unknown rule activations.
|
||||
type bitConditionChecker struct {
|
||||
bit uint32
|
||||
chain *BlockChain
|
||||
}
|
||||
|
||||
// Ensure the bitConditionChecker type implements the thresholdConditionChecker
|
||||
// interface.
|
||||
var _ thresholdConditionChecker = bitConditionChecker{}
|
||||
|
||||
// BeginTime returns the unix timestamp for the median block time after which
|
||||
// voting on a rule change starts (at the next window).
|
||||
//
|
||||
// Since this implementation checks for unknown rules, it returns 0 so the rule
|
||||
// is always treated as active.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c bitConditionChecker) BeginTime() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// EndTime returns the unix timestamp for the median block time after which an
|
||||
// attempted rule change fails if it has not already been locked in or
|
||||
// activated.
|
||||
//
|
||||
// Since this implementation checks for unknown rules, it returns the maximum
|
||||
// possible timestamp so the rule is always treated as active.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c bitConditionChecker) EndTime() uint64 {
|
||||
return math.MaxUint64
|
||||
}
|
||||
|
||||
// RuleChangeActivationThreshold is the number of blocks for which the condition
|
||||
// must be true in order to lock in a rule change.
|
||||
//
|
||||
// This implementation returns the value defined by the chain params the checker
|
||||
// is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c bitConditionChecker) RuleChangeActivationThreshold() uint32 {
|
||||
return c.chain.chainParams.RuleChangeActivationThreshold
|
||||
}
|
||||
|
||||
// MinerConfirmationWindow is the number of blocks in each threshold state
|
||||
// retarget window.
|
||||
//
|
||||
// This implementation returns the value defined by the chain params the checker
|
||||
// is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c bitConditionChecker) MinerConfirmationWindow() uint32 {
|
||||
return c.chain.chainParams.MinerConfirmationWindow
|
||||
}
|
||||
|
||||
// Condition returns true when the specific bit associated with the checker is
|
||||
// set and it's not supposed to be according to the expected version based on
|
||||
// the known deployments and the current state of the chain.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c bitConditionChecker) Condition(node *blockNode) (bool, error) {
|
||||
conditionMask := uint32(1) << c.bit
|
||||
version := uint32(node.version)
|
||||
if version&vbTopMask != vbTopBits {
|
||||
return false, nil
|
||||
}
|
||||
if version&conditionMask == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
expectedVersion, err := c.chain.calcNextBlockVersion(node.parent)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return uint32(expectedVersion)&conditionMask == 0, nil
|
||||
}
|
||||
|
||||
// deploymentChecker provides a thresholdConditionChecker which can be used to
|
||||
// test a specific deployment rule. This is required for properly detecting
|
||||
// and activating consensus rule changes.
|
||||
type deploymentChecker struct {
|
||||
deployment *chaincfg.ConsensusDeployment
|
||||
chain *BlockChain
|
||||
}
|
||||
|
||||
// Ensure the deploymentChecker type implements the thresholdConditionChecker
|
||||
// interface.
|
||||
var _ thresholdConditionChecker = deploymentChecker{}
|
||||
|
||||
// BeginTime returns the unix timestamp for the median block time after which
|
||||
// voting on a rule change starts (at the next window).
|
||||
//
|
||||
// This implementation returns the value defined by the specific deployment the
|
||||
// checker is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c deploymentChecker) BeginTime() uint64 {
|
||||
return c.deployment.StartTime
|
||||
}
|
||||
|
||||
// EndTime returns the unix timestamp for the median block time after which an
|
||||
// attempted rule change fails if it has not already been locked in or
|
||||
// activated.
|
||||
//
|
||||
// This implementation returns the value defined by the specific deployment the
|
||||
// checker is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c deploymentChecker) EndTime() uint64 {
|
||||
return c.deployment.ExpireTime
|
||||
}
|
||||
|
||||
// RuleChangeActivationThreshold is the number of blocks for which the condition
|
||||
// must be true in order to lock in a rule change.
|
||||
//
|
||||
// This implementation returns the value defined by the chain params the checker
|
||||
// is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c deploymentChecker) RuleChangeActivationThreshold() uint32 {
|
||||
return c.chain.chainParams.RuleChangeActivationThreshold
|
||||
}
|
||||
|
||||
// MinerConfirmationWindow is the number of blocks in each threshold state
|
||||
// retarget window.
|
||||
//
|
||||
// This implementation returns the value defined by the chain params the checker
|
||||
// is associated with.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c deploymentChecker) MinerConfirmationWindow() uint32 {
|
||||
return c.chain.chainParams.MinerConfirmationWindow
|
||||
}
|
||||
|
||||
// Condition returns true when the specific bit defined by the deployment
|
||||
// associated with the checker is set.
|
||||
//
|
||||
// This is part of the thresholdConditionChecker interface implementation.
|
||||
func (c deploymentChecker) Condition(node *blockNode) (bool, error) {
|
||||
conditionMask := uint32(1) << c.deployment.BitNumber
|
||||
version := uint32(node.version)
|
||||
return (version&vbTopMask == vbTopBits) && (version&conditionMask != 0),
|
||||
nil
|
||||
}
|
||||
|
||||
// calcNextBlockVersion calculates the expected version of the block after the
|
||||
// passed previous block node based on the state of started and locked in
|
||||
// rule change deployments.
|
||||
//
|
||||
// This function differs from the exported CalcNextBlockVersion in that the
|
||||
// exported version uses the current best chain as the previous block node
|
||||
// while this function accepts any block node.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) calcNextBlockVersion(prevNode *blockNode) (int32, error) {
|
||||
// Set the appropriate bits for each actively defined rule deployment
|
||||
// that is either in the process of being voted on, or locked in for the
|
||||
// activation at the next threshold window change.
|
||||
expectedVersion := uint32(vbTopBits)
|
||||
for id := 0; id < len(b.chainParams.Deployments); id++ {
|
||||
deployment := &b.chainParams.Deployments[id]
|
||||
cache := &b.deploymentCaches[id]
|
||||
checker := deploymentChecker{deployment: deployment, chain: b}
|
||||
state, err := b.thresholdState(prevNode, checker, cache)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if state == ThresholdStarted || state == ThresholdLockedIn {
|
||||
expectedVersion |= uint32(1) << deployment.BitNumber
|
||||
}
|
||||
}
|
||||
return int32(expectedVersion), nil
|
||||
}
|
||||
|
||||
// CalcNextBlockVersion calculates the expected version of the block after the
|
||||
// end of the current best chain based on the state of started and locked in
|
||||
// rule change deployments.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (b *BlockChain) CalcNextBlockVersion() (int32, error) {
|
||||
b.chainLock.Lock()
|
||||
version, err := b.calcNextBlockVersion(b.bestChain.Tip())
|
||||
b.chainLock.Unlock()
|
||||
return version, err
|
||||
}
|
||||
|
||||
// warnUnknownRuleActivations displays a warning when any unknown new rules are
|
||||
// either about to activate or have been activated. This will only happen once
|
||||
// when new rules have been activated and every block for those about to be
|
||||
// activated.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes)
|
||||
func (b *BlockChain) warnUnknownRuleActivations(node *blockNode) error {
|
||||
// Warn if any unknown new rules are either about to activate or have
|
||||
// already been activated.
|
||||
for bit := uint32(0); bit < vbNumBits; bit++ {
|
||||
checker := bitConditionChecker{bit: bit, chain: b}
|
||||
cache := &b.warningCaches[bit]
|
||||
state, err := b.thresholdState(node.parent, checker, cache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch state {
|
||||
case ThresholdActive:
|
||||
if !b.unknownRulesWarned {
|
||||
log.Warnf("Unknown new rules activated (bit %d)",
|
||||
bit)
|
||||
b.unknownRulesWarned = true
|
||||
}
|
||||
|
||||
case ThresholdLockedIn:
|
||||
window := int32(checker.MinerConfirmationWindow())
|
||||
activationHeight := window - (node.height % window)
|
||||
log.Warnf("Unknown new rules are about to activate in "+
|
||||
"%d blocks (bit %d)", activationHeight, bit)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// warnUnknownVersions logs a warning if a high enough percentage of the last
|
||||
// blocks have unexpected versions.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes)
|
||||
func (b *BlockChain) warnUnknownVersions(node *blockNode) error {
|
||||
// Nothing to do if already warned.
|
||||
if b.unknownVersionsWarned {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warn if enough previous blocks have unexpected versions.
|
||||
numUpgraded := uint32(0)
|
||||
for i := uint32(0); i < unknownVerNumToCheck && node != nil; i++ {
|
||||
expectedVersion, err := b.calcNextBlockVersion(node.parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expectedVersion > vbLegacyBlockVersion &&
|
||||
(node.version & ^expectedVersion) != 0 {
|
||||
|
||||
numUpgraded++
|
||||
}
|
||||
|
||||
node = node.parent
|
||||
}
|
||||
if numUpgraded > unknownVerWarnNum {
|
||||
log.Warn("Unknown block versions are being mined, so new " +
|
||||
"rules might be in effect. Are you running the " +
|
||||
"latest version of the software?")
|
||||
b.unknownVersionsWarned = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxBlockWeight defines the maximum block weight, where "block
|
||||
// weight" is interpreted as defined in BIP0141. A block's weight is
|
||||
// calculated as the sum of the of bytes in the existing transactions
|
||||
// and header, plus the weight of each byte within a transaction. The
|
||||
// weight of a "base" byte is 4, while the weight of a witness byte is
|
||||
// 1. As a result, for a block to be valid, the BlockWeight MUST be
|
||||
// less than, or equal to MaxBlockWeight.
|
||||
MaxBlockWeight = 4000000
|
||||
|
||||
// MaxBlockBaseSize is the maximum number of bytes within a block
|
||||
// which can be allocated to non-witness data.
|
||||
MaxBlockBaseSize = 1000000
|
||||
|
||||
// MaxBlockSigOpsCost is the maximum number of signature operations
|
||||
// allowed for a block. It is calculated via a weighted algorithm which
|
||||
// weights segregated witness sig ops lower than regular sig ops.
|
||||
MaxBlockSigOpsCost = 80000
|
||||
|
||||
// WitnessScaleFactor determines the level of "discount" witness data
|
||||
// receives compared to "base" data. A scale factor of 4, denotes that
|
||||
// witness data is 1/4 as cheap as regular non-witness data.
|
||||
WitnessScaleFactor = 4
|
||||
|
||||
// MinTxOutputWeight is the minimum possible weight for a transaction
|
||||
// output.
|
||||
MinTxOutputWeight = WitnessScaleFactor * wire.MinTxOutPayload
|
||||
|
||||
// MaxOutputsPerBlock is the maximum number of transaction outputs there
|
||||
// can be in a block of max weight size.
|
||||
MaxOutputsPerBlock = MaxBlockWeight / MinTxOutputWeight
|
||||
)
|
||||
|
||||
// GetBlockWeight computes the value of the weight metric for a given block.
|
||||
// Currently the weight metric is simply the sum of the block's serialized size
|
||||
// without any witness data scaled proportionally by the WitnessScaleFactor,
|
||||
// and the block's serialized size including any witness data.
|
||||
func GetBlockWeight(blk *btcutil.Block) int64 {
|
||||
msgBlock := blk.MsgBlock()
|
||||
|
||||
baseSize := msgBlock.SerializeSizeStripped()
|
||||
totalSize := msgBlock.SerializeSize()
|
||||
|
||||
// (baseSize * 3) + totalSize
|
||||
return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize)
|
||||
}
|
||||
|
||||
// GetTransactionWeight computes the value of the weight metric for a given
|
||||
// transaction. Currently the weight metric is simply the sum of the
|
||||
// transactions's serialized size without any witness data scaled
|
||||
// proportionally by the WitnessScaleFactor, and the transaction's serialized
|
||||
// size including any witness data.
|
||||
func GetTransactionWeight(tx *btcutil.Tx) int64 {
|
||||
msgTx := tx.MsgTx()
|
||||
|
||||
baseSize := msgTx.SerializeSizeStripped()
|
||||
totalSize := msgTx.SerializeSize()
|
||||
|
||||
// (baseSize * 3) + totalSize
|
||||
return int64((baseSize * (WitnessScaleFactor - 1)) + totalSize)
|
||||
}
|
||||
|
||||
// GetSigOpCost returns the unified sig op cost for the passed transaction
|
||||
// respecting current active soft-forks which modified sig op cost counting.
|
||||
// The unified sig op cost for a transaction is computed as the sum of: the
|
||||
// legacy sig op count scaled according to the WitnessScaleFactor, the sig op
|
||||
// count for all p2sh inputs scaled by the WitnessScaleFactor, and finally the
|
||||
// unscaled sig op count for any inputs spending witness programs.
|
||||
func GetSigOpCost(tx *btcutil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint, bip16, segWit bool) (int, error) {
|
||||
numSigOps := CountSigOps(tx) * WitnessScaleFactor
|
||||
if bip16 {
|
||||
numP2SHSigOps, err := CountP2SHSigOps(tx, isCoinBaseTx, utxoView)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
numSigOps += (numP2SHSigOps * WitnessScaleFactor)
|
||||
}
|
||||
|
||||
if segWit && !isCoinBaseTx {
|
||||
msgTx := tx.MsgTx()
|
||||
for txInIndex, txIn := range msgTx.TxIn {
|
||||
// Ensure the referenced output is available and hasn't
|
||||
// already been spent.
|
||||
utxo := utxoView.LookupEntry(txIn.PreviousOutPoint)
|
||||
if utxo == nil || utxo.IsSpent() {
|
||||
str := fmt.Sprintf("output %v referenced from "+
|
||||
"transaction %s:%d either does not "+
|
||||
"exist or has already been spent",
|
||||
txIn.PreviousOutPoint, tx.Hash(),
|
||||
txInIndex)
|
||||
return 0, ruleError(ErrMissingTxOut, str)
|
||||
}
|
||||
|
||||
witness := txIn.Witness
|
||||
sigScript := txIn.SignatureScript
|
||||
pkScript := utxo.PkScript()
|
||||
numSigOps += txscript.GetWitnessSigOpCount(sigScript, pkScript, witness)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return numSigOps, nil
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
btcec
|
||||
=====
|
||||
|
||||
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcec)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://godoc.org/github.com/btcsuite/btcd/btcec?status.png)](http://godoc.org/github.com/btcsuite/btcd/btcec)
|
||||
|
||||
Package btcec implements elliptic curve cryptography needed for working with
|
||||
Bitcoin (secp256k1 only for now). It is designed so that it may be used with the
|
||||
standard crypto/ecdsa packages provided with go. A comprehensive suite of test
|
||||
is provided to ensure proper functionality. Package btcec was originally based
|
||||
on work from ThePiachu which is licensed under the same terms as Go, but it has
|
||||
signficantly diverged since then. The btcsuite developers original is licensed
|
||||
under the liberal ISC license.
|
||||
|
||||
Although this package was primarily written for btcd, it has intentionally been
|
||||
designed so it can be used as a standalone package for any projects needing to
|
||||
use secp256k1 elliptic curve cryptography.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/btcec
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [Sign Message](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage)
|
||||
Demonstrates signing a message with a secp256k1 private key that is first
|
||||
parsed form raw bytes and serializing the generated signature.
|
||||
|
||||
* [Verify Signature](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature)
|
||||
Demonstrates verifying a secp256k1 signature against a public key that is
|
||||
first parsed from raw bytes. The signature is also parsed from raw bytes.
|
||||
|
||||
* [Encryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage)
|
||||
Demonstrates encrypting a message for a public key that is first parsed from
|
||||
raw bytes, then decrypting it using the corresponding private key.
|
||||
|
||||
* [Decryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage)
|
||||
Demonstrates decrypting a message using a private key that is first parsed
|
||||
from raw bytes.
|
||||
|
||||
## GPG Verification Key
|
||||
|
||||
All official release tags are signed by Conformal so users can ensure the code
|
||||
has not been tampered with and is coming from the btcsuite developers. To
|
||||
verify the signature perform the following:
|
||||
|
||||
- Download the public key from the Conformal website at
|
||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
||||
|
||||
- Import the public key into your GPG keyring:
|
||||
```bash
|
||||
gpg --import GIT-GPG-KEY-conformal.txt
|
||||
```
|
||||
|
||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
||||
placeholder for the specific tag:
|
||||
```bash
|
||||
git tag -v TAG_NAME
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package btcec is licensed under the [copyfree](http://copyfree.org) ISC License
|
||||
except for btcec.go and btcec_test.go which is under the same license as Go.
|
||||
|
|
@ -0,0 +1,958 @@
|
|||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Copyright 2011 ThePiachu. All rights reserved.
|
||||
// Copyright 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
// References:
|
||||
// [SECG]: Recommended Elliptic Curve Domain Parameters
|
||||
// http://www.secg.org/sec2-v2.pdf
|
||||
//
|
||||
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
|
||||
|
||||
// This package operates, internally, on Jacobian coordinates. For a given
|
||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
||||
// calculation can be performed within the transform (as in ScalarMult and
|
||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
||||
// reverse the transform than to operate in affine coordinates.
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"math/big"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// fieldOne is simply the integer 1 in field representation. It is
|
||||
// used to avoid needing to create it multiple times during the internal
|
||||
// arithmetic.
|
||||
fieldOne = new(fieldVal).SetInt(1)
|
||||
)
|
||||
|
||||
// KoblitzCurve supports a koblitz curve implementation that fits the ECC Curve
|
||||
// interface from crypto/elliptic.
|
||||
type KoblitzCurve struct {
|
||||
*elliptic.CurveParams
|
||||
q *big.Int
|
||||
H int // cofactor of the curve.
|
||||
halfOrder *big.Int // half the order N
|
||||
|
||||
// byteSize is simply the bit size / 8 and is provided for convenience
|
||||
// since it is calculated repeatedly.
|
||||
byteSize int
|
||||
|
||||
// bytePoints
|
||||
bytePoints *[32][256][3]fieldVal
|
||||
|
||||
// The next 6 values are used specifically for endomorphism
|
||||
// optimizations in ScalarMult.
|
||||
|
||||
// lambda must fulfill lambda^3 = 1 mod N where N is the order of G.
|
||||
lambda *big.Int
|
||||
|
||||
// beta must fulfill beta^3 = 1 mod P where P is the prime field of the
|
||||
// curve.
|
||||
beta *fieldVal
|
||||
|
||||
// See the EndomorphismVectors in gensecp256k1.go to see how these are
|
||||
// derived.
|
||||
a1 *big.Int
|
||||
b1 *big.Int
|
||||
a2 *big.Int
|
||||
b2 *big.Int
|
||||
}
|
||||
|
||||
// Params returns the parameters for the curve.
|
||||
func (curve *KoblitzCurve) Params() *elliptic.CurveParams {
|
||||
return curve.CurveParams
|
||||
}
|
||||
|
||||
// bigAffineToField takes an affine point (x, y) as big integers and converts
|
||||
// it to an affine point as field values.
|
||||
func (curve *KoblitzCurve) bigAffineToField(x, y *big.Int) (*fieldVal, *fieldVal) {
|
||||
x3, y3 := new(fieldVal), new(fieldVal)
|
||||
x3.SetByteSlice(x.Bytes())
|
||||
y3.SetByteSlice(y.Bytes())
|
||||
|
||||
return x3, y3
|
||||
}
|
||||
|
||||
// fieldJacobianToBigAffine takes a Jacobian point (x, y, z) as field values and
|
||||
// converts it to an affine point as big integers.
|
||||
func (curve *KoblitzCurve) fieldJacobianToBigAffine(x, y, z *fieldVal) (*big.Int, *big.Int) {
|
||||
// Inversions are expensive and both point addition and point doubling
|
||||
// are faster when working with points that have a z value of one. So,
|
||||
// if the point needs to be converted to affine, go ahead and normalize
|
||||
// the point itself at the same time as the calculation is the same.
|
||||
var zInv, tempZ fieldVal
|
||||
zInv.Set(z).Inverse() // zInv = Z^-1
|
||||
tempZ.SquareVal(&zInv) // tempZ = Z^-2
|
||||
x.Mul(&tempZ) // X = X/Z^2 (mag: 1)
|
||||
y.Mul(tempZ.Mul(&zInv)) // Y = Y/Z^3 (mag: 1)
|
||||
z.SetInt(1) // Z = 1 (mag: 1)
|
||||
|
||||
// Normalize the x and y values.
|
||||
x.Normalize()
|
||||
y.Normalize()
|
||||
|
||||
// Convert the field values for the now affine point to big.Ints.
|
||||
x3, y3 := new(big.Int), new(big.Int)
|
||||
x3.SetBytes(x.Bytes()[:])
|
||||
y3.SetBytes(y.Bytes()[:])
|
||||
return x3, y3
|
||||
}
|
||||
|
||||
// IsOnCurve returns boolean if the point (x,y) is on the curve.
|
||||
// Part of the elliptic.Curve interface. This function differs from the
|
||||
// crypto/elliptic algorithm since a = 0 not -3.
|
||||
func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool {
|
||||
// Convert big ints to field values for faster arithmetic.
|
||||
fx, fy := curve.bigAffineToField(x, y)
|
||||
|
||||
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
|
||||
y2 := new(fieldVal).SquareVal(fy).Normalize()
|
||||
result := new(fieldVal).SquareVal(fx).Mul(fx).AddInt(7).Normalize()
|
||||
return y2.Equals(result)
|
||||
}
|
||||
|
||||
// addZ1AndZ2EqualsOne adds two Jacobian points that are already known to have
|
||||
// z values of 1 and stores the result in (x3, y3, z3). That is to say
|
||||
// (x1, y1, 1) + (x2, y2, 1) = (x3, y3, z3). It performs faster addition than
|
||||
// the generic add routine since less arithmetic is needed due to the ability to
|
||||
// avoid the z value multiplications.
|
||||
func (curve *KoblitzCurve) addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) {
|
||||
// To compute the point addition efficiently, this implementation splits
|
||||
// the equation into intermediate elements which are used to minimize
|
||||
// the number of field multiplications using the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// H = X2-X1, HH = H^2, I = 4*HH, J = H*I, r = 2*(Y2-Y1), V = X1*I
|
||||
// X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = 2*H
|
||||
//
|
||||
// This results in a cost of 4 field multiplications, 2 field squarings,
|
||||
// 6 field additions, and 5 integer multiplications.
|
||||
|
||||
// When the x coordinates are the same for two points on the curve, the
|
||||
// y coordinates either must be the same, in which case it is point
|
||||
// doubling, or they are opposite and the result is the point at
|
||||
// infinity per the group law for elliptic curve cryptography.
|
||||
x1.Normalize()
|
||||
y1.Normalize()
|
||||
x2.Normalize()
|
||||
y2.Normalize()
|
||||
if x1.Equals(x2) {
|
||||
if y1.Equals(y2) {
|
||||
// Since x1 == x2 and y1 == y2, point doubling must be
|
||||
// done, otherwise the addition would end up dividing
|
||||
// by zero.
|
||||
curve.doubleJacobian(x1, y1, z1, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// Since x1 == x2 and y1 == -y2, the sum is the point at
|
||||
// infinity per the group law.
|
||||
x3.SetInt(0)
|
||||
y3.SetInt(0)
|
||||
z3.SetInt(0)
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate X3, Y3, and Z3 according to the intermediate elements
|
||||
// breakdown above.
|
||||
var h, i, j, r, v fieldVal
|
||||
var negJ, neg2V, negX3 fieldVal
|
||||
h.Set(x1).Negate(1).Add(x2) // H = X2-X1 (mag: 3)
|
||||
i.SquareVal(&h).MulInt(4) // I = 4*H^2 (mag: 4)
|
||||
j.Mul2(&h, &i) // J = H*I (mag: 1)
|
||||
r.Set(y1).Negate(1).Add(y2).MulInt(2) // r = 2*(Y2-Y1) (mag: 6)
|
||||
v.Mul2(x1, &i) // V = X1*I (mag: 1)
|
||||
negJ.Set(&j).Negate(1) // negJ = -J (mag: 2)
|
||||
neg2V.Set(&v).MulInt(2).Negate(2) // neg2V = -(2*V) (mag: 3)
|
||||
x3.Set(&r).Square().Add(&negJ).Add(&neg2V) // X3 = r^2-J-2*V (mag: 6)
|
||||
negX3.Set(x3).Negate(6) // negX3 = -X3 (mag: 7)
|
||||
j.Mul(y1).MulInt(2).Negate(2) // J = -(2*Y1*J) (mag: 3)
|
||||
y3.Set(&v).Add(&negX3).Mul(&r).Add(&j) // Y3 = r*(V-X3)-2*Y1*J (mag: 4)
|
||||
z3.Set(&h).MulInt(2) // Z3 = 2*H (mag: 6)
|
||||
|
||||
// Normalize the resulting field values to a magnitude of 1 as needed.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
z3.Normalize()
|
||||
}
|
||||
|
||||
// addZ1EqualsZ2 adds two Jacobian points that are already known to have the
|
||||
// same z value and stores the result in (x3, y3, z3). That is to say
|
||||
// (x1, y1, z1) + (x2, y2, z1) = (x3, y3, z3). It performs faster addition than
|
||||
// the generic add routine since less arithmetic is needed due to the known
|
||||
// equivalence.
|
||||
func (curve *KoblitzCurve) addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) {
|
||||
// To compute the point addition efficiently, this implementation splits
|
||||
// the equation into intermediate elements which are used to minimize
|
||||
// the number of field multiplications using a slightly modified version
|
||||
// of the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-mmadd-2007-bl
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// A = X2-X1, B = A^2, C=Y2-Y1, D = C^2, E = X1*B, F = X2*B
|
||||
// X3 = D-E-F, Y3 = C*(E-X3)-Y1*(F-E), Z3 = Z1*A
|
||||
//
|
||||
// This results in a cost of 5 field multiplications, 2 field squarings,
|
||||
// 9 field additions, and 0 integer multiplications.
|
||||
|
||||
// When the x coordinates are the same for two points on the curve, the
|
||||
// y coordinates either must be the same, in which case it is point
|
||||
// doubling, or they are opposite and the result is the point at
|
||||
// infinity per the group law for elliptic curve cryptography.
|
||||
x1.Normalize()
|
||||
y1.Normalize()
|
||||
x2.Normalize()
|
||||
y2.Normalize()
|
||||
if x1.Equals(x2) {
|
||||
if y1.Equals(y2) {
|
||||
// Since x1 == x2 and y1 == y2, point doubling must be
|
||||
// done, otherwise the addition would end up dividing
|
||||
// by zero.
|
||||
curve.doubleJacobian(x1, y1, z1, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// Since x1 == x2 and y1 == -y2, the sum is the point at
|
||||
// infinity per the group law.
|
||||
x3.SetInt(0)
|
||||
y3.SetInt(0)
|
||||
z3.SetInt(0)
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate X3, Y3, and Z3 according to the intermediate elements
|
||||
// breakdown above.
|
||||
var a, b, c, d, e, f fieldVal
|
||||
var negX1, negY1, negE, negX3 fieldVal
|
||||
negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2)
|
||||
negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2)
|
||||
a.Set(&negX1).Add(x2) // A = X2-X1 (mag: 3)
|
||||
b.SquareVal(&a) // B = A^2 (mag: 1)
|
||||
c.Set(&negY1).Add(y2) // C = Y2-Y1 (mag: 3)
|
||||
d.SquareVal(&c) // D = C^2 (mag: 1)
|
||||
e.Mul2(x1, &b) // E = X1*B (mag: 1)
|
||||
negE.Set(&e).Negate(1) // negE = -E (mag: 2)
|
||||
f.Mul2(x2, &b) // F = X2*B (mag: 1)
|
||||
x3.Add2(&e, &f).Negate(3).Add(&d) // X3 = D-E-F (mag: 5)
|
||||
negX3.Set(x3).Negate(5).Normalize() // negX3 = -X3 (mag: 1)
|
||||
y3.Set(y1).Mul(f.Add(&negE)).Negate(3) // Y3 = -(Y1*(F-E)) (mag: 4)
|
||||
y3.Add(e.Add(&negX3).Mul(&c)) // Y3 = C*(E-X3)+Y3 (mag: 5)
|
||||
z3.Mul2(z1, &a) // Z3 = Z1*A (mag: 1)
|
||||
|
||||
// Normalize the resulting field values to a magnitude of 1 as needed.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
}
|
||||
|
||||
// addZ2EqualsOne adds two Jacobian points when the second point is already
|
||||
// known to have a z value of 1 (and the z value for the first point is not 1)
|
||||
// and stores the result in (x3, y3, z3). That is to say (x1, y1, z1) +
|
||||
// (x2, y2, 1) = (x3, y3, z3). It performs faster addition than the generic
|
||||
// add routine since less arithmetic is needed due to the ability to avoid
|
||||
// multiplications by the second point's z value.
|
||||
func (curve *KoblitzCurve) addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3 *fieldVal) {
|
||||
// To compute the point addition efficiently, this implementation splits
|
||||
// the equation into intermediate elements which are used to minimize
|
||||
// the number of field multiplications using the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-madd-2007-bl
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// Z1Z1 = Z1^2, U2 = X2*Z1Z1, S2 = Y2*Z1*Z1Z1, H = U2-X1, HH = H^2,
|
||||
// I = 4*HH, J = H*I, r = 2*(S2-Y1), V = X1*I
|
||||
// X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*Y1*J, Z3 = (Z1+H)^2-Z1Z1-HH
|
||||
//
|
||||
// This results in a cost of 7 field multiplications, 4 field squarings,
|
||||
// 9 field additions, and 4 integer multiplications.
|
||||
|
||||
// When the x coordinates are the same for two points on the curve, the
|
||||
// y coordinates either must be the same, in which case it is point
|
||||
// doubling, or they are opposite and the result is the point at
|
||||
// infinity per the group law for elliptic curve cryptography. Since
|
||||
// any number of Jacobian coordinates can represent the same affine
|
||||
// point, the x and y values need to be converted to like terms. Due to
|
||||
// the assumption made for this function that the second point has a z
|
||||
// value of 1 (z2=1), the first point is already "converted".
|
||||
var z1z1, u2, s2 fieldVal
|
||||
x1.Normalize()
|
||||
y1.Normalize()
|
||||
z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1)
|
||||
u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1)
|
||||
s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1)
|
||||
if x1.Equals(&u2) {
|
||||
if y1.Equals(&s2) {
|
||||
// Since x1 == x2 and y1 == y2, point doubling must be
|
||||
// done, otherwise the addition would end up dividing
|
||||
// by zero.
|
||||
curve.doubleJacobian(x1, y1, z1, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// Since x1 == x2 and y1 == -y2, the sum is the point at
|
||||
// infinity per the group law.
|
||||
x3.SetInt(0)
|
||||
y3.SetInt(0)
|
||||
z3.SetInt(0)
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate X3, Y3, and Z3 according to the intermediate elements
|
||||
// breakdown above.
|
||||
var h, hh, i, j, r, rr, v fieldVal
|
||||
var negX1, negY1, negX3 fieldVal
|
||||
negX1.Set(x1).Negate(1) // negX1 = -X1 (mag: 2)
|
||||
h.Add2(&u2, &negX1) // H = U2-X1 (mag: 3)
|
||||
hh.SquareVal(&h) // HH = H^2 (mag: 1)
|
||||
i.Set(&hh).MulInt(4) // I = 4 * HH (mag: 4)
|
||||
j.Mul2(&h, &i) // J = H*I (mag: 1)
|
||||
negY1.Set(y1).Negate(1) // negY1 = -Y1 (mag: 2)
|
||||
r.Set(&s2).Add(&negY1).MulInt(2) // r = 2*(S2-Y1) (mag: 6)
|
||||
rr.SquareVal(&r) // rr = r^2 (mag: 1)
|
||||
v.Mul2(x1, &i) // V = X1*I (mag: 1)
|
||||
x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4)
|
||||
x3.Add(&rr) // X3 = r^2+X3 (mag: 5)
|
||||
negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6)
|
||||
y3.Set(y1).Mul(&j).MulInt(2).Negate(2) // Y3 = -(2*Y1*J) (mag: 3)
|
||||
y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4)
|
||||
z3.Add2(z1, &h).Square() // Z3 = (Z1+H)^2 (mag: 1)
|
||||
z3.Add(z1z1.Add(&hh).Negate(2)) // Z3 = Z3-(Z1Z1+HH) (mag: 4)
|
||||
|
||||
// Normalize the resulting field values to a magnitude of 1 as needed.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
z3.Normalize()
|
||||
}
|
||||
|
||||
// addGeneric adds two Jacobian points (x1, y1, z1) and (x2, y2, z2) without any
|
||||
// assumptions about the z values of the two points and stores the result in
|
||||
// (x3, y3, z3). That is to say (x1, y1, z1) + (x2, y2, z2) = (x3, y3, z3). It
|
||||
// is the slowest of the add routines due to requiring the most arithmetic.
|
||||
func (curve *KoblitzCurve) addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) {
|
||||
// To compute the point addition efficiently, this implementation splits
|
||||
// the equation into intermediate elements which are used to minimize
|
||||
// the number of field multiplications using the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// Z1Z1 = Z1^2, Z2Z2 = Z2^2, U1 = X1*Z2Z2, U2 = X2*Z1Z1, S1 = Y1*Z2*Z2Z2
|
||||
// S2 = Y2*Z1*Z1Z1, H = U2-U1, I = (2*H)^2, J = H*I, r = 2*(S2-S1)
|
||||
// V = U1*I
|
||||
// X3 = r^2-J-2*V, Y3 = r*(V-X3)-2*S1*J, Z3 = ((Z1+Z2)^2-Z1Z1-Z2Z2)*H
|
||||
//
|
||||
// This results in a cost of 11 field multiplications, 5 field squarings,
|
||||
// 9 field additions, and 4 integer multiplications.
|
||||
|
||||
// When the x coordinates are the same for two points on the curve, the
|
||||
// y coordinates either must be the same, in which case it is point
|
||||
// doubling, or they are opposite and the result is the point at
|
||||
// infinity. Since any number of Jacobian coordinates can represent the
|
||||
// same affine point, the x and y values need to be converted to like
|
||||
// terms.
|
||||
var z1z1, z2z2, u1, u2, s1, s2 fieldVal
|
||||
z1z1.SquareVal(z1) // Z1Z1 = Z1^2 (mag: 1)
|
||||
z2z2.SquareVal(z2) // Z2Z2 = Z2^2 (mag: 1)
|
||||
u1.Set(x1).Mul(&z2z2).Normalize() // U1 = X1*Z2Z2 (mag: 1)
|
||||
u2.Set(x2).Mul(&z1z1).Normalize() // U2 = X2*Z1Z1 (mag: 1)
|
||||
s1.Set(y1).Mul(&z2z2).Mul(z2).Normalize() // S1 = Y1*Z2*Z2Z2 (mag: 1)
|
||||
s2.Set(y2).Mul(&z1z1).Mul(z1).Normalize() // S2 = Y2*Z1*Z1Z1 (mag: 1)
|
||||
if u1.Equals(&u2) {
|
||||
if s1.Equals(&s2) {
|
||||
// Since x1 == x2 and y1 == y2, point doubling must be
|
||||
// done, otherwise the addition would end up dividing
|
||||
// by zero.
|
||||
curve.doubleJacobian(x1, y1, z1, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// Since x1 == x2 and y1 == -y2, the sum is the point at
|
||||
// infinity per the group law.
|
||||
x3.SetInt(0)
|
||||
y3.SetInt(0)
|
||||
z3.SetInt(0)
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate X3, Y3, and Z3 according to the intermediate elements
|
||||
// breakdown above.
|
||||
var h, i, j, r, rr, v fieldVal
|
||||
var negU1, negS1, negX3 fieldVal
|
||||
negU1.Set(&u1).Negate(1) // negU1 = -U1 (mag: 2)
|
||||
h.Add2(&u2, &negU1) // H = U2-U1 (mag: 3)
|
||||
i.Set(&h).MulInt(2).Square() // I = (2*H)^2 (mag: 2)
|
||||
j.Mul2(&h, &i) // J = H*I (mag: 1)
|
||||
negS1.Set(&s1).Negate(1) // negS1 = -S1 (mag: 2)
|
||||
r.Set(&s2).Add(&negS1).MulInt(2) // r = 2*(S2-S1) (mag: 6)
|
||||
rr.SquareVal(&r) // rr = r^2 (mag: 1)
|
||||
v.Mul2(&u1, &i) // V = U1*I (mag: 1)
|
||||
x3.Set(&v).MulInt(2).Add(&j).Negate(3) // X3 = -(J+2*V) (mag: 4)
|
||||
x3.Add(&rr) // X3 = r^2+X3 (mag: 5)
|
||||
negX3.Set(x3).Negate(5) // negX3 = -X3 (mag: 6)
|
||||
y3.Mul2(&s1, &j).MulInt(2).Negate(2) // Y3 = -(2*S1*J) (mag: 3)
|
||||
y3.Add(v.Add(&negX3).Mul(&r)) // Y3 = r*(V-X3)+Y3 (mag: 4)
|
||||
z3.Add2(z1, z2).Square() // Z3 = (Z1+Z2)^2 (mag: 1)
|
||||
z3.Add(z1z1.Add(&z2z2).Negate(2)) // Z3 = Z3-(Z1Z1+Z2Z2) (mag: 4)
|
||||
z3.Mul(&h) // Z3 = Z3*H (mag: 1)
|
||||
|
||||
// Normalize the resulting field values to a magnitude of 1 as needed.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
}
|
||||
|
||||
// addJacobian adds the passed Jacobian points (x1, y1, z1) and (x2, y2, z2)
|
||||
// together and stores the result in (x3, y3, z3).
|
||||
func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3 *fieldVal) {
|
||||
// A point at infinity is the identity according to the group law for
|
||||
// elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P.
|
||||
if (x1.IsZero() && y1.IsZero()) || z1.IsZero() {
|
||||
x3.Set(x2)
|
||||
y3.Set(y2)
|
||||
z3.Set(z2)
|
||||
return
|
||||
}
|
||||
if (x2.IsZero() && y2.IsZero()) || z2.IsZero() {
|
||||
x3.Set(x1)
|
||||
y3.Set(y1)
|
||||
z3.Set(z1)
|
||||
return
|
||||
}
|
||||
|
||||
// Faster point addition can be achieved when certain assumptions are
|
||||
// met. For example, when both points have the same z value, arithmetic
|
||||
// on the z values can be avoided. This section thus checks for these
|
||||
// conditions and calls an appropriate add function which is accelerated
|
||||
// by using those assumptions.
|
||||
z1.Normalize()
|
||||
z2.Normalize()
|
||||
isZ1One := z1.Equals(fieldOne)
|
||||
isZ2One := z2.Equals(fieldOne)
|
||||
switch {
|
||||
case isZ1One && isZ2One:
|
||||
curve.addZ1AndZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3)
|
||||
return
|
||||
case z1.Equals(z2):
|
||||
curve.addZ1EqualsZ2(x1, y1, z1, x2, y2, x3, y3, z3)
|
||||
return
|
||||
case isZ2One:
|
||||
curve.addZ2EqualsOne(x1, y1, z1, x2, y2, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// None of the above assumptions are true, so fall back to generic
|
||||
// point addition.
|
||||
curve.addGeneric(x1, y1, z1, x2, y2, z2, x3, y3, z3)
|
||||
}
|
||||
|
||||
// Add returns the sum of (x1,y1) and (x2,y2). Part of the elliptic.Curve
|
||||
// interface.
|
||||
func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
// A point at infinity is the identity according to the group law for
|
||||
// elliptic curve cryptography. Thus, ∞ + P = P and P + ∞ = P.
|
||||
if x1.Sign() == 0 && y1.Sign() == 0 {
|
||||
return x2, y2
|
||||
}
|
||||
if x2.Sign() == 0 && y2.Sign() == 0 {
|
||||
return x1, y1
|
||||
}
|
||||
|
||||
// Convert the affine coordinates from big integers to field values
|
||||
// and do the point addition in Jacobian projective space.
|
||||
fx1, fy1 := curve.bigAffineToField(x1, y1)
|
||||
fx2, fy2 := curve.bigAffineToField(x2, y2)
|
||||
fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
fOne := new(fieldVal).SetInt(1)
|
||||
curve.addJacobian(fx1, fy1, fOne, fx2, fy2, fOne, fx3, fy3, fz3)
|
||||
|
||||
// Convert the Jacobian coordinate field values back to affine big
|
||||
// integers.
|
||||
return curve.fieldJacobianToBigAffine(fx3, fy3, fz3)
|
||||
}
|
||||
|
||||
// doubleZ1EqualsOne performs point doubling on the passed Jacobian point
|
||||
// when the point is already known to have a z value of 1 and stores
|
||||
// the result in (x3, y3, z3). That is to say (x3, y3, z3) = 2*(x1, y1, 1). It
|
||||
// performs faster point doubling than the generic routine since less arithmetic
|
||||
// is needed due to the ability to avoid multiplication by the z value.
|
||||
func (curve *KoblitzCurve) doubleZ1EqualsOne(x1, y1, x3, y3, z3 *fieldVal) {
|
||||
// This function uses the assumptions that z1 is 1, thus the point
|
||||
// doubling formulas reduce to:
|
||||
//
|
||||
// X3 = (3*X1^2)^2 - 8*X1*Y1^2
|
||||
// Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4
|
||||
// Z3 = 2*Y1
|
||||
//
|
||||
// To compute the above efficiently, this implementation splits the
|
||||
// equation into intermediate elements which are used to minimize the
|
||||
// number of field multiplications in favor of field squarings which
|
||||
// are roughly 35% faster than field multiplications with the current
|
||||
// implementation at the time this was written.
|
||||
//
|
||||
// This uses a slightly modified version of the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-mdbl-2007-bl
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C)
|
||||
// E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C
|
||||
// Z3 = 2*Y1
|
||||
//
|
||||
// This results in a cost of 1 field multiplication, 5 field squarings,
|
||||
// 6 field additions, and 5 integer multiplications.
|
||||
var a, b, c, d, e, f fieldVal
|
||||
z3.Set(y1).MulInt(2) // Z3 = 2*Y1 (mag: 2)
|
||||
a.SquareVal(x1) // A = X1^2 (mag: 1)
|
||||
b.SquareVal(y1) // B = Y1^2 (mag: 1)
|
||||
c.SquareVal(&b) // C = B^2 (mag: 1)
|
||||
b.Add(x1).Square() // B = (X1+B)^2 (mag: 1)
|
||||
d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3)
|
||||
d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8)
|
||||
e.Set(&a).MulInt(3) // E = 3*A (mag: 3)
|
||||
f.SquareVal(&e) // F = E^2 (mag: 1)
|
||||
x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17)
|
||||
x3.Add(&f) // X3 = F+X3 (mag: 18)
|
||||
f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1)
|
||||
y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9)
|
||||
y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10)
|
||||
|
||||
// Normalize the field values back to a magnitude of 1.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
z3.Normalize()
|
||||
}
|
||||
|
||||
// doubleGeneric performs point doubling on the passed Jacobian point without
|
||||
// any assumptions about the z value and stores the result in (x3, y3, z3).
|
||||
// That is to say (x3, y3, z3) = 2*(x1, y1, z1). It is the slowest of the point
|
||||
// doubling routines due to requiring the most arithmetic.
|
||||
func (curve *KoblitzCurve) doubleGeneric(x1, y1, z1, x3, y3, z3 *fieldVal) {
|
||||
// Point doubling formula for Jacobian coordinates for the secp256k1
|
||||
// curve:
|
||||
// X3 = (3*X1^2)^2 - 8*X1*Y1^2
|
||||
// Y3 = (3*X1^2)*(4*X1*Y1^2 - X3) - 8*Y1^4
|
||||
// Z3 = 2*Y1*Z1
|
||||
//
|
||||
// To compute the above efficiently, this implementation splits the
|
||||
// equation into intermediate elements which are used to minimize the
|
||||
// number of field multiplications in favor of field squarings which
|
||||
// are roughly 35% faster than field multiplications with the current
|
||||
// implementation at the time this was written.
|
||||
//
|
||||
// This uses a slightly modified version of the method shown at:
|
||||
// http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
//
|
||||
// In particular it performs the calculations using the following:
|
||||
// A = X1^2, B = Y1^2, C = B^2, D = 2*((X1+B)^2-A-C)
|
||||
// E = 3*A, F = E^2, X3 = F-2*D, Y3 = E*(D-X3)-8*C
|
||||
// Z3 = 2*Y1*Z1
|
||||
//
|
||||
// This results in a cost of 1 field multiplication, 5 field squarings,
|
||||
// 6 field additions, and 5 integer multiplications.
|
||||
var a, b, c, d, e, f fieldVal
|
||||
z3.Mul2(y1, z1).MulInt(2) // Z3 = 2*Y1*Z1 (mag: 2)
|
||||
a.SquareVal(x1) // A = X1^2 (mag: 1)
|
||||
b.SquareVal(y1) // B = Y1^2 (mag: 1)
|
||||
c.SquareVal(&b) // C = B^2 (mag: 1)
|
||||
b.Add(x1).Square() // B = (X1+B)^2 (mag: 1)
|
||||
d.Set(&a).Add(&c).Negate(2) // D = -(A+C) (mag: 3)
|
||||
d.Add(&b).MulInt(2) // D = 2*(B+D)(mag: 8)
|
||||
e.Set(&a).MulInt(3) // E = 3*A (mag: 3)
|
||||
f.SquareVal(&e) // F = E^2 (mag: 1)
|
||||
x3.Set(&d).MulInt(2).Negate(16) // X3 = -(2*D) (mag: 17)
|
||||
x3.Add(&f) // X3 = F+X3 (mag: 18)
|
||||
f.Set(x3).Negate(18).Add(&d).Normalize() // F = D-X3 (mag: 1)
|
||||
y3.Set(&c).MulInt(8).Negate(8) // Y3 = -(8*C) (mag: 9)
|
||||
y3.Add(f.Mul(&e)) // Y3 = E*F+Y3 (mag: 10)
|
||||
|
||||
// Normalize the field values back to a magnitude of 1.
|
||||
x3.Normalize()
|
||||
y3.Normalize()
|
||||
z3.Normalize()
|
||||
}
|
||||
|
||||
// doubleJacobian doubles the passed Jacobian point (x1, y1, z1) and stores the
|
||||
// result in (x3, y3, z3).
|
||||
func (curve *KoblitzCurve) doubleJacobian(x1, y1, z1, x3, y3, z3 *fieldVal) {
|
||||
// Doubling a point at infinity is still infinity.
|
||||
if y1.IsZero() || z1.IsZero() {
|
||||
x3.SetInt(0)
|
||||
y3.SetInt(0)
|
||||
z3.SetInt(0)
|
||||
return
|
||||
}
|
||||
|
||||
// Slightly faster point doubling can be achieved when the z value is 1
|
||||
// by avoiding the multiplication on the z value. This section calls
|
||||
// a point doubling function which is accelerated by using that
|
||||
// assumption when possible.
|
||||
if z1.Normalize().Equals(fieldOne) {
|
||||
curve.doubleZ1EqualsOne(x1, y1, x3, y3, z3)
|
||||
return
|
||||
}
|
||||
|
||||
// Fall back to generic point doubling which works with arbitrary z
|
||||
// values.
|
||||
curve.doubleGeneric(x1, y1, z1, x3, y3, z3)
|
||||
}
|
||||
|
||||
// Double returns 2*(x1,y1). Part of the elliptic.Curve interface.
|
||||
func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
if y1.Sign() == 0 {
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
|
||||
// Convert the affine coordinates from big integers to field values
|
||||
// and do the point doubling in Jacobian projective space.
|
||||
fx1, fy1 := curve.bigAffineToField(x1, y1)
|
||||
fx3, fy3, fz3 := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
fOne := new(fieldVal).SetInt(1)
|
||||
curve.doubleJacobian(fx1, fy1, fOne, fx3, fy3, fz3)
|
||||
|
||||
// Convert the Jacobian coordinate field values back to affine big
|
||||
// integers.
|
||||
return curve.fieldJacobianToBigAffine(fx3, fy3, fz3)
|
||||
}
|
||||
|
||||
// splitK returns a balanced length-two representation of k and their signs.
|
||||
// This is algorithm 3.74 from [GECC].
|
||||
//
|
||||
// One thing of note about this algorithm is that no matter what c1 and c2 are,
|
||||
// the final equation of k = k1 + k2 * lambda (mod n) will hold. This is
|
||||
// provable mathematically due to how a1/b1/a2/b2 are computed.
|
||||
//
|
||||
// c1 and c2 are chosen to minimize the max(k1,k2).
|
||||
func (curve *KoblitzCurve) splitK(k []byte) ([]byte, []byte, int, int) {
|
||||
// All math here is done with big.Int, which is slow.
|
||||
// At some point, it might be useful to write something similar to
|
||||
// fieldVal but for N instead of P as the prime field if this ends up
|
||||
// being a bottleneck.
|
||||
bigIntK := new(big.Int)
|
||||
c1, c2 := new(big.Int), new(big.Int)
|
||||
tmp1, tmp2 := new(big.Int), new(big.Int)
|
||||
k1, k2 := new(big.Int), new(big.Int)
|
||||
|
||||
bigIntK.SetBytes(k)
|
||||
// c1 = round(b2 * k / n) from step 4.
|
||||
// Rounding isn't really necessary and costs too much, hence skipped
|
||||
c1.Mul(curve.b2, bigIntK)
|
||||
c1.Div(c1, curve.N)
|
||||
// c2 = round(b1 * k / n) from step 4 (sign reversed to optimize one step)
|
||||
// Rounding isn't really necessary and costs too much, hence skipped
|
||||
c2.Mul(curve.b1, bigIntK)
|
||||
c2.Div(c2, curve.N)
|
||||
// k1 = k - c1 * a1 - c2 * a2 from step 5 (note c2's sign is reversed)
|
||||
tmp1.Mul(c1, curve.a1)
|
||||
tmp2.Mul(c2, curve.a2)
|
||||
k1.Sub(bigIntK, tmp1)
|
||||
k1.Add(k1, tmp2)
|
||||
// k2 = - c1 * b1 - c2 * b2 from step 5 (note c2's sign is reversed)
|
||||
tmp1.Mul(c1, curve.b1)
|
||||
tmp2.Mul(c2, curve.b2)
|
||||
k2.Sub(tmp2, tmp1)
|
||||
|
||||
// Note Bytes() throws out the sign of k1 and k2. This matters
|
||||
// since k1 and/or k2 can be negative. Hence, we pass that
|
||||
// back separately.
|
||||
return k1.Bytes(), k2.Bytes(), k1.Sign(), k2.Sign()
|
||||
}
|
||||
|
||||
// moduloReduce reduces k from more than 32 bytes to 32 bytes and under. This
|
||||
// is done by doing a simple modulo curve.N. We can do this since G^N = 1 and
|
||||
// thus any other valid point on the elliptic curve has the same order.
|
||||
func (curve *KoblitzCurve) moduloReduce(k []byte) []byte {
|
||||
// Since the order of G is curve.N, we can use a much smaller number
|
||||
// by doing modulo curve.N
|
||||
if len(k) > curve.byteSize {
|
||||
// Reduce k by performing modulo curve.N.
|
||||
tmpK := new(big.Int).SetBytes(k)
|
||||
tmpK.Mod(tmpK, curve.N)
|
||||
return tmpK.Bytes()
|
||||
}
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
// NAF takes a positive integer k and returns the Non-Adjacent Form (NAF) as two
|
||||
// byte slices. The first is where 1s will be. The second is where -1s will
|
||||
// be. NAF is convenient in that on average, only 1/3rd of its values are
|
||||
// non-zero. This is algorithm 3.30 from [GECC].
|
||||
//
|
||||
// Essentially, this makes it possible to minimize the number of operations
|
||||
// since the resulting ints returned will be at least 50% 0s.
|
||||
func NAF(k []byte) ([]byte, []byte) {
|
||||
// The essence of this algorithm is that whenever we have consecutive 1s
|
||||
// in the binary, we want to put a -1 in the lowest bit and get a bunch
|
||||
// of 0s up to the highest bit of consecutive 1s. This is due to this
|
||||
// identity:
|
||||
// 2^n + 2^(n-1) + 2^(n-2) + ... + 2^(n-k) = 2^(n+1) - 2^(n-k)
|
||||
//
|
||||
// The algorithm thus may need to go 1 more bit than the length of the
|
||||
// bits we actually have, hence bits being 1 bit longer than was
|
||||
// necessary. Since we need to know whether adding will cause a carry,
|
||||
// we go from right-to-left in this addition.
|
||||
var carry, curIsOne, nextIsOne bool
|
||||
// these default to zero
|
||||
retPos := make([]byte, len(k)+1)
|
||||
retNeg := make([]byte, len(k)+1)
|
||||
for i := len(k) - 1; i >= 0; i-- {
|
||||
curByte := k[i]
|
||||
for j := uint(0); j < 8; j++ {
|
||||
curIsOne = curByte&1 == 1
|
||||
if j == 7 {
|
||||
if i == 0 {
|
||||
nextIsOne = false
|
||||
} else {
|
||||
nextIsOne = k[i-1]&1 == 1
|
||||
}
|
||||
} else {
|
||||
nextIsOne = curByte&2 == 2
|
||||
}
|
||||
if carry {
|
||||
if curIsOne {
|
||||
// This bit is 1, so continue to carry
|
||||
// and don't need to do anything.
|
||||
} else {
|
||||
// We've hit a 0 after some number of
|
||||
// 1s.
|
||||
if nextIsOne {
|
||||
// Start carrying again since
|
||||
// a new sequence of 1s is
|
||||
// starting.
|
||||
retNeg[i+1] += 1 << j
|
||||
} else {
|
||||
// Stop carrying since 1s have
|
||||
// stopped.
|
||||
carry = false
|
||||
retPos[i+1] += 1 << j
|
||||
}
|
||||
}
|
||||
} else if curIsOne {
|
||||
if nextIsOne {
|
||||
// If this is the start of at least 2
|
||||
// consecutive 1s, set the current one
|
||||
// to -1 and start carrying.
|
||||
retNeg[i+1] += 1 << j
|
||||
carry = true
|
||||
} else {
|
||||
// This is a singleton, not consecutive
|
||||
// 1s.
|
||||
retPos[i+1] += 1 << j
|
||||
}
|
||||
}
|
||||
curByte >>= 1
|
||||
}
|
||||
}
|
||||
if carry {
|
||||
retPos[0] = 1
|
||||
return retPos, retNeg
|
||||
}
|
||||
return retPos[1:], retNeg[1:]
|
||||
}
|
||||
|
||||
// ScalarMult returns k*(Bx, By) where k is a big endian integer.
|
||||
// Part of the elliptic.Curve interface.
|
||||
func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
|
||||
// Point Q = ∞ (point at infinity).
|
||||
qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
|
||||
// Decompose K into k1 and k2 in order to halve the number of EC ops.
|
||||
// See Algorithm 3.74 in [GECC].
|
||||
k1, k2, signK1, signK2 := curve.splitK(curve.moduloReduce(k))
|
||||
|
||||
// The main equation here to remember is:
|
||||
// k * P = k1 * P + k2 * ϕ(P)
|
||||
//
|
||||
// P1 below is P in the equation, P2 below is ϕ(P) in the equation
|
||||
p1x, p1y := curve.bigAffineToField(Bx, By)
|
||||
p1yNeg := new(fieldVal).NegateVal(p1y, 1)
|
||||
p1z := new(fieldVal).SetInt(1)
|
||||
|
||||
// NOTE: ϕ(x,y) = (βx,y). The Jacobian z coordinate is 1, so this math
|
||||
// goes through.
|
||||
p2x := new(fieldVal).Mul2(p1x, curve.beta)
|
||||
p2y := new(fieldVal).Set(p1y)
|
||||
p2yNeg := new(fieldVal).NegateVal(p2y, 1)
|
||||
p2z := new(fieldVal).SetInt(1)
|
||||
|
||||
// Flip the positive and negative values of the points as needed
|
||||
// depending on the signs of k1 and k2. As mentioned in the equation
|
||||
// above, each of k1 and k2 are multiplied by the respective point.
|
||||
// Since -k * P is the same thing as k * -P, and the group law for
|
||||
// elliptic curves states that P(x, y) = -P(x, -y), it's faster and
|
||||
// simplifies the code to just make the point negative.
|
||||
if signK1 == -1 {
|
||||
p1y, p1yNeg = p1yNeg, p1y
|
||||
}
|
||||
if signK2 == -1 {
|
||||
p2y, p2yNeg = p2yNeg, p2y
|
||||
}
|
||||
|
||||
// NAF versions of k1 and k2 should have a lot more zeros.
|
||||
//
|
||||
// The Pos version of the bytes contain the +1s and the Neg versions
|
||||
// contain the -1s.
|
||||
k1PosNAF, k1NegNAF := NAF(k1)
|
||||
k2PosNAF, k2NegNAF := NAF(k2)
|
||||
k1Len := len(k1PosNAF)
|
||||
k2Len := len(k2PosNAF)
|
||||
|
||||
m := k1Len
|
||||
if m < k2Len {
|
||||
m = k2Len
|
||||
}
|
||||
|
||||
// Add left-to-right using the NAF optimization. See algorithm 3.77
|
||||
// from [GECC]. This should be faster overall since there will be a lot
|
||||
// more instances of 0, hence reducing the number of Jacobian additions
|
||||
// at the cost of 1 possible extra doubling.
|
||||
var k1BytePos, k1ByteNeg, k2BytePos, k2ByteNeg byte
|
||||
for i := 0; i < m; i++ {
|
||||
// Since we're going left-to-right, pad the front with 0s.
|
||||
if i < m-k1Len {
|
||||
k1BytePos = 0
|
||||
k1ByteNeg = 0
|
||||
} else {
|
||||
k1BytePos = k1PosNAF[i-(m-k1Len)]
|
||||
k1ByteNeg = k1NegNAF[i-(m-k1Len)]
|
||||
}
|
||||
if i < m-k2Len {
|
||||
k2BytePos = 0
|
||||
k2ByteNeg = 0
|
||||
} else {
|
||||
k2BytePos = k2PosNAF[i-(m-k2Len)]
|
||||
k2ByteNeg = k2NegNAF[i-(m-k2Len)]
|
||||
}
|
||||
|
||||
for j := 7; j >= 0; j-- {
|
||||
// Q = 2 * Q
|
||||
curve.doubleJacobian(qx, qy, qz, qx, qy, qz)
|
||||
|
||||
if k1BytePos&0x80 == 0x80 {
|
||||
curve.addJacobian(qx, qy, qz, p1x, p1y, p1z,
|
||||
qx, qy, qz)
|
||||
} else if k1ByteNeg&0x80 == 0x80 {
|
||||
curve.addJacobian(qx, qy, qz, p1x, p1yNeg, p1z,
|
||||
qx, qy, qz)
|
||||
}
|
||||
|
||||
if k2BytePos&0x80 == 0x80 {
|
||||
curve.addJacobian(qx, qy, qz, p2x, p2y, p2z,
|
||||
qx, qy, qz)
|
||||
} else if k2ByteNeg&0x80 == 0x80 {
|
||||
curve.addJacobian(qx, qy, qz, p2x, p2yNeg, p2z,
|
||||
qx, qy, qz)
|
||||
}
|
||||
k1BytePos <<= 1
|
||||
k1ByteNeg <<= 1
|
||||
k2BytePos <<= 1
|
||||
k2ByteNeg <<= 1
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the Jacobian coordinate field values back to affine big.Ints.
|
||||
return curve.fieldJacobianToBigAffine(qx, qy, qz)
|
||||
}
|
||||
|
||||
// ScalarBaseMult returns k*G where G is the base point of the group and k is a
|
||||
// big endian integer.
|
||||
// Part of the elliptic.Curve interface.
|
||||
func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
newK := curve.moduloReduce(k)
|
||||
diff := len(curve.bytePoints) - len(newK)
|
||||
|
||||
// Point Q = ∞ (point at infinity).
|
||||
qx, qy, qz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
|
||||
// curve.bytePoints has all 256 byte points for each 8-bit window. The
|
||||
// strategy is to add up the byte points. This is best understood by
|
||||
// expressing k in base-256 which it already sort of is.
|
||||
// Each "digit" in the 8-bit window can be looked up using bytePoints
|
||||
// and added together.
|
||||
for i, byteVal := range newK {
|
||||
p := curve.bytePoints[diff+i][byteVal]
|
||||
curve.addJacobian(qx, qy, qz, &p[0], &p[1], &p[2], qx, qy, qz)
|
||||
}
|
||||
return curve.fieldJacobianToBigAffine(qx, qy, qz)
|
||||
}
|
||||
|
||||
// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating
|
||||
// square roots via exponention.
|
||||
func (curve *KoblitzCurve) QPlus1Div4() *big.Int {
|
||||
return curve.q
|
||||
}
|
||||
|
||||
var initonce sync.Once
|
||||
var secp256k1 KoblitzCurve
|
||||
|
||||
func initAll() {
|
||||
initS256()
|
||||
}
|
||||
|
||||
// fromHex converts the passed hex string into a big integer pointer and will
|
||||
// panic is there is an error. This is only provided for the hard-coded
|
||||
// constants so errors in the source code can bet detected. It will only (and
|
||||
// must only) be called for initialization purposes.
|
||||
func fromHex(s string) *big.Int {
|
||||
r, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid hex in source file: " + s)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func initS256() {
|
||||
// Curve parameters taken from [SECG] section 2.4.1.
|
||||
secp256k1.CurveParams = new(elliptic.CurveParams)
|
||||
secp256k1.P = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")
|
||||
secp256k1.N = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")
|
||||
secp256k1.B = fromHex("0000000000000000000000000000000000000000000000000000000000000007")
|
||||
secp256k1.Gx = fromHex("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")
|
||||
secp256k1.Gy = fromHex("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8")
|
||||
secp256k1.BitSize = 256
|
||||
secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P,
|
||||
big.NewInt(1)), big.NewInt(4))
|
||||
secp256k1.H = 1
|
||||
secp256k1.halfOrder = new(big.Int).Rsh(secp256k1.N, 1)
|
||||
|
||||
// Provided for convenience since this gets computed repeatedly.
|
||||
secp256k1.byteSize = secp256k1.BitSize / 8
|
||||
|
||||
// Deserialize and set the pre-computed table used to accelerate scalar
|
||||
// base multiplication. This is hard-coded data, so any errors are
|
||||
// panics because it means something is wrong in the source code.
|
||||
if err := loadS256BytePoints(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Next 6 constants are from Hal Finney's bitcointalk.org post:
|
||||
// https://bitcointalk.org/index.php?topic=3238.msg45565#msg45565
|
||||
// May he rest in peace.
|
||||
//
|
||||
// They have also been independently derived from the code in the
|
||||
// EndomorphismVectors function in gensecp256k1.go.
|
||||
secp256k1.lambda = fromHex("5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72")
|
||||
secp256k1.beta = new(fieldVal).SetHex("7AE96A2B657C07106E64479EAC3434E99CF0497512F58995C1396C28719501EE")
|
||||
secp256k1.a1 = fromHex("3086D221A7D46BCDE86C90E49284EB15")
|
||||
secp256k1.b1 = fromHex("-E4437ED6010E88286F547FA90ABFE4C3")
|
||||
secp256k1.a2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8")
|
||||
secp256k1.b2 = fromHex("3086D221A7D46BCDE86C90E49284EB15")
|
||||
|
||||
// Alternatively, we can use the parameters below, however, they seem
|
||||
// to be about 8% slower.
|
||||
// secp256k1.lambda = fromHex("AC9C52B33FA3CF1F5AD9E3FD77ED9BA4A880B9FC8EC739C2E0CFC810B51283CE")
|
||||
// secp256k1.beta = new(fieldVal).SetHex("851695D49A83F8EF919BB86153CBCB16630FB68AED0A766A3EC693D68E6AFA40")
|
||||
// secp256k1.a1 = fromHex("E4437ED6010E88286F547FA90ABFE4C3")
|
||||
// secp256k1.b1 = fromHex("-3086D221A7D46BCDE86C90E49284EB15")
|
||||
// secp256k1.a2 = fromHex("3086D221A7D46BCDE86C90E49284EB15")
|
||||
// secp256k1.b2 = fromHex("114CA50F7A8E2F3F657C1108D9D44CFD8")
|
||||
}
|
||||
|
||||
// S256 returns a Curve which implements secp256k1.
|
||||
func S256() *KoblitzCurve {
|
||||
initonce.Do(initAll)
|
||||
return &secp256k1
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
// Copyright (c) 2015-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidMAC occurs when Message Authentication Check (MAC) fails
|
||||
// during decryption. This happens because of either invalid private key or
|
||||
// corrupt ciphertext.
|
||||
ErrInvalidMAC = errors.New("invalid mac hash")
|
||||
|
||||
// errInputTooShort occurs when the input ciphertext to the Decrypt
|
||||
// function is less than 134 bytes long.
|
||||
errInputTooShort = errors.New("ciphertext too short")
|
||||
|
||||
// errUnsupportedCurve occurs when the first two bytes of the encrypted
|
||||
// text aren't 0x02CA (= 712 = secp256k1, from OpenSSL).
|
||||
errUnsupportedCurve = errors.New("unsupported curve")
|
||||
|
||||
errInvalidXLength = errors.New("invalid X length, must be 32")
|
||||
errInvalidYLength = errors.New("invalid Y length, must be 32")
|
||||
errInvalidPadding = errors.New("invalid PKCS#7 padding")
|
||||
|
||||
// 0x02CA = 714
|
||||
ciphCurveBytes = [2]byte{0x02, 0xCA}
|
||||
// 0x20 = 32
|
||||
ciphCoordLength = [2]byte{0x00, 0x20}
|
||||
)
|
||||
|
||||
// GenerateSharedSecret generates a shared secret based on a private key and a
|
||||
// public key using Diffie-Hellman key exchange (ECDH) (RFC 4753).
|
||||
// RFC5903 Section 9 states we should only return x.
|
||||
func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte {
|
||||
x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes())
|
||||
return x.Bytes()
|
||||
}
|
||||
|
||||
// Encrypt encrypts data for the target public key using AES-256-CBC. It also
|
||||
// generates a private key (the pubkey of which is also in the output). The only
|
||||
// supported curve is secp256k1. The `structure' that it encodes everything into
|
||||
// is:
|
||||
//
|
||||
// struct {
|
||||
// // Initialization Vector used for AES-256-CBC
|
||||
// IV [16]byte
|
||||
// // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX +
|
||||
// // len_of_pubkeyY(2) + pubkeyY (curve = 714)
|
||||
// PublicKey [70]byte
|
||||
// // Cipher text
|
||||
// Data []byte
|
||||
// // HMAC-SHA-256 Message Authentication Code
|
||||
// HMAC [32]byte
|
||||
// }
|
||||
//
|
||||
// The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer
|
||||
// to section 5.8.1 of ANSI X9.63 for rationale on this format.
|
||||
func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) {
|
||||
ephemeral, err := NewPrivateKey(S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ecdhKey := GenerateSharedSecret(ephemeral, pubkey)
|
||||
derivedKey := sha512.Sum512(ecdhKey)
|
||||
keyE := derivedKey[:32]
|
||||
keyM := derivedKey[32:]
|
||||
|
||||
paddedIn := addPKCSPadding(in)
|
||||
// IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256
|
||||
out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size)
|
||||
iv := out[:aes.BlockSize]
|
||||
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// start writing public key
|
||||
pb := ephemeral.PubKey().SerializeUncompressed()
|
||||
offset := aes.BlockSize
|
||||
|
||||
// curve and X length
|
||||
copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...))
|
||||
offset += 4
|
||||
// X
|
||||
copy(out[offset:offset+32], pb[1:33])
|
||||
offset += 32
|
||||
// Y length
|
||||
copy(out[offset:offset+2], ciphCoordLength[:])
|
||||
offset += 2
|
||||
// Y
|
||||
copy(out[offset:offset+32], pb[33:])
|
||||
offset += 32
|
||||
|
||||
// start encryption
|
||||
block, err := aes.NewCipher(keyE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn)
|
||||
|
||||
// start HMAC-SHA-256
|
||||
hm := hmac.New(sha256.New, keyM)
|
||||
hm.Write(out[:len(out)-sha256.Size]) // everything is hashed
|
||||
copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts data that was encrypted using the Encrypt function.
|
||||
func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) {
|
||||
// IV + Curve params/X/Y + 1 block + HMAC-256
|
||||
if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size {
|
||||
return nil, errInputTooShort
|
||||
}
|
||||
|
||||
// read iv
|
||||
iv := in[:aes.BlockSize]
|
||||
offset := aes.BlockSize
|
||||
|
||||
// start reading pubkey
|
||||
if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) {
|
||||
return nil, errUnsupportedCurve
|
||||
}
|
||||
offset += 2
|
||||
|
||||
if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) {
|
||||
return nil, errInvalidXLength
|
||||
}
|
||||
offset += 2
|
||||
|
||||
xBytes := in[offset : offset+32]
|
||||
offset += 32
|
||||
|
||||
if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) {
|
||||
return nil, errInvalidYLength
|
||||
}
|
||||
offset += 2
|
||||
|
||||
yBytes := in[offset : offset+32]
|
||||
offset += 32
|
||||
|
||||
pb := make([]byte, 65)
|
||||
pb[0] = byte(0x04) // uncompressed
|
||||
copy(pb[1:33], xBytes)
|
||||
copy(pb[33:], yBytes)
|
||||
// check if (X, Y) lies on the curve and create a Pubkey if it does
|
||||
pubkey, err := ParsePubKey(pb, S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check for cipher text length
|
||||
if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 {
|
||||
return nil, errInvalidPadding // not padded to 16 bytes
|
||||
}
|
||||
|
||||
// read hmac
|
||||
messageMAC := in[len(in)-sha256.Size:]
|
||||
|
||||
// generate shared secret
|
||||
ecdhKey := GenerateSharedSecret(priv, pubkey)
|
||||
derivedKey := sha512.Sum512(ecdhKey)
|
||||
keyE := derivedKey[:32]
|
||||
keyM := derivedKey[32:]
|
||||
|
||||
// verify mac
|
||||
hm := hmac.New(sha256.New, keyM)
|
||||
hm.Write(in[:len(in)-sha256.Size]) // everything is hashed
|
||||
expectedMAC := hm.Sum(nil)
|
||||
if !hmac.Equal(messageMAC, expectedMAC) {
|
||||
return nil, ErrInvalidMAC
|
||||
}
|
||||
|
||||
// start decryption
|
||||
block, err := aes.NewCipher(keyE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
// same length as ciphertext
|
||||
plaintext := make([]byte, len(in)-offset-sha256.Size)
|
||||
mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size])
|
||||
|
||||
return removePKCSPadding(plaintext)
|
||||
}
|
||||
|
||||
// Implement PKCS#7 padding with block size of 16 (AES block size).
|
||||
|
||||
// addPKCSPadding adds padding to a block of data
|
||||
func addPKCSPadding(src []byte) []byte {
|
||||
padding := aes.BlockSize - len(src)%aes.BlockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(src, padtext...)
|
||||
}
|
||||
|
||||
// removePKCSPadding removes padding from data that was added with addPKCSPadding
|
||||
func removePKCSPadding(src []byte) ([]byte, error) {
|
||||
length := len(src)
|
||||
padLength := int(src[length-1])
|
||||
if padLength > aes.BlockSize || length < aes.BlockSize {
|
||||
return nil, errInvalidPadding
|
||||
}
|
||||
|
||||
return src[:length-padLength], nil
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package btcec implements support for the elliptic curves needed for bitcoin.
|
||||
|
||||
Bitcoin uses elliptic curve cryptography using koblitz curves
|
||||
(specifically secp256k1) for cryptographic functions. See
|
||||
http://www.secg.org/collateral/sec2_final.pdf for details on the
|
||||
standard.
|
||||
|
||||
This package provides the data structures and functions implementing the
|
||||
crypto/elliptic Curve interface in order to permit using these curves
|
||||
with the standard crypto/ecdsa package provided with go. Helper
|
||||
functionality is provided to parse signatures and public keys from
|
||||
standard formats. It was designed for use with btcd, but should be
|
||||
general enough for other uses of elliptic curve crypto. It was originally based
|
||||
on some initial work by ThePiachu, but has significantly diverged since then.
|
||||
*/
|
||||
package btcec
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file is ignored during the regular build due to the following build tag.
|
||||
// It is called by go generate and used to automatically generate pre-computed
|
||||
// tables used to accelerate operations.
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fi, err := os.Create("secp256k1.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer fi.Close()
|
||||
|
||||
// Compress the serialized byte points.
|
||||
serialized := btcec.S256().SerializedBytePoints()
|
||||
var compressed bytes.Buffer
|
||||
w := zlib.NewWriter(&compressed)
|
||||
if _, err := w.Write(serialized); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
w.Close()
|
||||
|
||||
// Encode the compressed byte points with base64.
|
||||
encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len()))
|
||||
base64.StdEncoding.Encode(encoded, compressed.Bytes())
|
||||
|
||||
fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers")
|
||||
fmt.Fprintln(fi, "// Use of this source code is governed by an ISC")
|
||||
fmt.Fprintln(fi, "// license that can be found in the LICENSE file.")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintln(fi, "package btcec")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)")
|
||||
fmt.Fprintln(fi, "// DO NOT EDIT")
|
||||
fmt.Fprintln(fi)
|
||||
fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded))
|
||||
|
||||
a1, b1, a2, b2 := btcec.S256().EndomorphismVectors()
|
||||
fmt.Println("The following values are the computed linearly " +
|
||||
"independent vectors needed to make use of the secp256k1 " +
|
||||
"endomorphism:")
|
||||
fmt.Printf("a1: %x\n", a1)
|
||||
fmt.Printf("b1: %x\n", b1)
|
||||
fmt.Printf("a2: %x\n", a2)
|
||||
fmt.Printf("b2: %x\n", b2)
|
||||
}
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright (c) 2014-2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file is ignored during the regular build due to the following build tag.
|
||||
// This build tag is set during go generate.
|
||||
// +build gensecp256k1
|
||||
|
||||
package btcec
|
||||
|
||||
// References:
|
||||
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// secp256k1BytePoints are dummy points used so the code which generates the
|
||||
// real values can compile.
|
||||
var secp256k1BytePoints = ""
|
||||
|
||||
// getDoublingPoints returns all the possible G^(2^i) for i in
|
||||
// 0..n-1 where n is the curve's bit size (256 in the case of secp256k1)
|
||||
// the coordinates are recorded as Jacobian coordinates.
|
||||
func (curve *KoblitzCurve) getDoublingPoints() [][3]fieldVal {
|
||||
doublingPoints := make([][3]fieldVal, curve.BitSize)
|
||||
|
||||
// initialize px, py, pz to the Jacobian coordinates for the base point
|
||||
px, py := curve.bigAffineToField(curve.Gx, curve.Gy)
|
||||
pz := new(fieldVal).SetInt(1)
|
||||
for i := 0; i < curve.BitSize; i++ {
|
||||
doublingPoints[i] = [3]fieldVal{*px, *py, *pz}
|
||||
// P = 2*P
|
||||
curve.doubleJacobian(px, py, pz, px, py, pz)
|
||||
}
|
||||
return doublingPoints
|
||||
}
|
||||
|
||||
// SerializedBytePoints returns a serialized byte slice which contains all of
|
||||
// the possible points per 8-bit window. This is used to when generating
|
||||
// secp256k1.go.
|
||||
func (curve *KoblitzCurve) SerializedBytePoints() []byte {
|
||||
doublingPoints := curve.getDoublingPoints()
|
||||
|
||||
// Segregate the bits into byte-sized windows
|
||||
serialized := make([]byte, curve.byteSize*256*3*10*4)
|
||||
offset := 0
|
||||
for byteNum := 0; byteNum < curve.byteSize; byteNum++ {
|
||||
// Grab the 8 bits that make up this byte from doublingPoints.
|
||||
startingBit := 8 * (curve.byteSize - byteNum - 1)
|
||||
computingPoints := doublingPoints[startingBit : startingBit+8]
|
||||
|
||||
// Compute all points in this window and serialize them.
|
||||
for i := 0; i < 256; i++ {
|
||||
px, py, pz := new(fieldVal), new(fieldVal), new(fieldVal)
|
||||
for j := 0; j < 8; j++ {
|
||||
if i>>uint(j)&1 == 1 {
|
||||
curve.addJacobian(px, py, pz, &computingPoints[j][0],
|
||||
&computingPoints[j][1], &computingPoints[j][2], px, py, pz)
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], px.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], py.n[i])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
binary.LittleEndian.PutUint32(serialized[offset:], pz.n[i])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// sqrt returns the square root of the provided big integer using Newton's
|
||||
// method. It's only compiled and used during generation of pre-computed
|
||||
// values, so speed is not a huge concern.
|
||||
func sqrt(n *big.Int) *big.Int {
|
||||
// Initial guess = 2^(log_2(n)/2)
|
||||
guess := big.NewInt(2)
|
||||
guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil)
|
||||
|
||||
// Now refine using Newton's method.
|
||||
big2 := big.NewInt(2)
|
||||
prevGuess := big.NewInt(0)
|
||||
for {
|
||||
prevGuess.Set(guess)
|
||||
guess.Add(guess, new(big.Int).Div(n, guess))
|
||||
guess.Div(guess, big2)
|
||||
if guess.Cmp(prevGuess) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return guess
|
||||
}
|
||||
|
||||
// EndomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to
|
||||
// generate the linearly independent vectors needed to generate a balanced
|
||||
// length-two representation of a multiplier such that k = k1 + k2λ (mod N) and
|
||||
// returns them. Since the values will always be the same given the fact that N
|
||||
// and λ are fixed, the final results can be accelerated by storing the
|
||||
// precomputed values with the curve.
|
||||
func (curve *KoblitzCurve) EndomorphismVectors() (a1, b1, a2, b2 *big.Int) {
|
||||
bigMinus1 := big.NewInt(-1)
|
||||
|
||||
// This section uses an extended Euclidean algorithm to generate a
|
||||
// sequence of equations:
|
||||
// s[i] * N + t[i] * λ = r[i]
|
||||
|
||||
nSqrt := sqrt(curve.N)
|
||||
u, v := new(big.Int).Set(curve.N), new(big.Int).Set(curve.lambda)
|
||||
x1, y1 := big.NewInt(1), big.NewInt(0)
|
||||
x2, y2 := big.NewInt(0), big.NewInt(1)
|
||||
q, r := new(big.Int), new(big.Int)
|
||||
qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int)
|
||||
s, t := new(big.Int), new(big.Int)
|
||||
ri, ti := new(big.Int), new(big.Int)
|
||||
a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int)
|
||||
found, oneMore := false, false
|
||||
for u.Sign() != 0 {
|
||||
// q = v/u
|
||||
q.Div(v, u)
|
||||
|
||||
// r = v - q*u
|
||||
qu.Mul(q, u)
|
||||
r.Sub(v, qu)
|
||||
|
||||
// s = x2 - q*x1
|
||||
qx1.Mul(q, x1)
|
||||
s.Sub(x2, qx1)
|
||||
|
||||
// t = y2 - q*y1
|
||||
qy1.Mul(q, y1)
|
||||
t.Sub(y2, qy1)
|
||||
|
||||
// v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t
|
||||
v.Set(u)
|
||||
u.Set(r)
|
||||
x2.Set(x1)
|
||||
x1.Set(s)
|
||||
y2.Set(y1)
|
||||
y1.Set(t)
|
||||
|
||||
// As soon as the remainder is less than the sqrt of n, the
|
||||
// values of a1 and b1 are known.
|
||||
if !found && r.Cmp(nSqrt) < 0 {
|
||||
// When this condition executes ri and ti represent the
|
||||
// r[i] and t[i] values such that i is the greatest
|
||||
// index for which r >= sqrt(n). Meanwhile, the current
|
||||
// r and t values are r[i+1] and t[i+1], respectively.
|
||||
|
||||
// a1 = r[i+1], b1 = -t[i+1]
|
||||
a1.Set(r)
|
||||
b1.Mul(t, bigMinus1)
|
||||
found = true
|
||||
oneMore = true
|
||||
|
||||
// Skip to the next iteration so ri and ti are not
|
||||
// modified.
|
||||
continue
|
||||
|
||||
} else if oneMore {
|
||||
// When this condition executes ri and ti still
|
||||
// represent the r[i] and t[i] values while the current
|
||||
// r and t are r[i+2] and t[i+2], respectively.
|
||||
|
||||
// sum1 = r[i]^2 + t[i]^2
|
||||
rSquared := new(big.Int).Mul(ri, ri)
|
||||
tSquared := new(big.Int).Mul(ti, ti)
|
||||
sum1 := new(big.Int).Add(rSquared, tSquared)
|
||||
|
||||
// sum2 = r[i+2]^2 + t[i+2]^2
|
||||
r2Squared := new(big.Int).Mul(r, r)
|
||||
t2Squared := new(big.Int).Mul(t, t)
|
||||
sum2 := new(big.Int).Add(r2Squared, t2Squared)
|
||||
|
||||
// if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2)
|
||||
if sum1.Cmp(sum2) <= 0 {
|
||||
// a2 = r[i], b2 = -t[i]
|
||||
a2.Set(ri)
|
||||
b2.Mul(ti, bigMinus1)
|
||||
} else {
|
||||
// a2 = r[i+2], b2 = -t[i+2]
|
||||
a2.Set(r)
|
||||
b2.Mul(t, bigMinus1)
|
||||
}
|
||||
|
||||
// All done.
|
||||
break
|
||||
}
|
||||
|
||||
ri.Set(r)
|
||||
ti.Set(t)
|
||||
}
|
||||
|
||||
return a1, b1, a2, b2
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"compress/zlib"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate go run -tags gensecp256k1 genprecomps.go
|
||||
|
||||
// loadS256BytePoints decompresses and deserializes the pre-computed byte points
|
||||
// used to accelerate scalar base multiplication for the secp256k1 curve. This
|
||||
// approach is used since it allows the compile to use significantly less ram
|
||||
// and be performed much faster than it is with hard-coding the final in-memory
|
||||
// data structure. At the same time, it is quite fast to generate the in-memory
|
||||
// data structure at init time with this approach versus computing the table.
|
||||
func loadS256BytePoints() error {
|
||||
// There will be no byte points to load when generating them.
|
||||
bp := secp256k1BytePoints
|
||||
if len(bp) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decompress the pre-computed table used to accelerate scalar base
|
||||
// multiplication.
|
||||
decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp))
|
||||
r, err := zlib.NewReader(decoder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serialized, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Deserialize the precomputed byte points and set the curve to them.
|
||||
offset := 0
|
||||
var bytePoints [32][256][3]fieldVal
|
||||
for byteNum := 0; byteNum < 32; byteNum++ {
|
||||
// All points in this window.
|
||||
for i := 0; i < 256; i++ {
|
||||
px := &bytePoints[byteNum][i][0]
|
||||
py := &bytePoints[byteNum][i][1]
|
||||
pz := &bytePoints[byteNum][i][2]
|
||||
for i := 0; i < 10; i++ {
|
||||
px.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
py.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
secp256k1.bytePoints = &bytePoints
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) 2013-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// PrivateKey wraps an ecdsa.PrivateKey as a convenience mainly for signing
|
||||
// things with the the private key without having to directly import the ecdsa
|
||||
// package.
|
||||
type PrivateKey ecdsa.PrivateKey
|
||||
|
||||
// PrivKeyFromBytes returns a private and public key for `curve' based on the
|
||||
// private key passed as an argument as a byte slice.
|
||||
func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey,
|
||||
*PublicKey) {
|
||||
x, y := curve.ScalarBaseMult(pk)
|
||||
|
||||
priv := &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: x,
|
||||
Y: y,
|
||||
},
|
||||
D: new(big.Int).SetBytes(pk),
|
||||
}
|
||||
|
||||
return (*PrivateKey)(priv), (*PublicKey)(&priv.PublicKey)
|
||||
}
|
||||
|
||||
// NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey
|
||||
// instead of the normal ecdsa.PrivateKey.
|
||||
func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) {
|
||||
key, err := ecdsa.GenerateKey(curve, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*PrivateKey)(key), nil
|
||||
}
|
||||
|
||||
// PubKey returns the PublicKey corresponding to this private key.
|
||||
func (p *PrivateKey) PubKey() *PublicKey {
|
||||
return (*PublicKey)(&p.PublicKey)
|
||||
}
|
||||
|
||||
// ToECDSA returns the private key as a *ecdsa.PrivateKey.
|
||||
func (p *PrivateKey) ToECDSA() *ecdsa.PrivateKey {
|
||||
return (*ecdsa.PrivateKey)(p)
|
||||
}
|
||||
|
||||
// Sign generates an ECDSA signature for the provided hash (which should be the result
|
||||
// of hashing a larger message) using the private key. Produced signature
|
||||
// is deterministic (same message and same key yield the same signature) and canonical
|
||||
// in accordance with RFC6979 and BIP0062.
|
||||
func (p *PrivateKey) Sign(hash []byte) (*Signature, error) {
|
||||
return signRFC6979(p, hash)
|
||||
}
|
||||
|
||||
// PrivKeyBytesLen defines the length in bytes of a serialized private key.
|
||||
const PrivKeyBytesLen = 32
|
||||
|
||||
// Serialize returns the private key number d as a big-endian binary-encoded
|
||||
// number, padded to a length of 32 bytes.
|
||||
func (p *PrivateKey) Serialize() []byte {
|
||||
b := make([]byte, 0, PrivKeyBytesLen)
|
||||
return paddedAppend(PrivKeyBytesLen, b, p.ToECDSA().D.Bytes())
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// These constants define the lengths of serialized public keys.
|
||||
const (
|
||||
PubKeyBytesLenCompressed = 33
|
||||
PubKeyBytesLenUncompressed = 65
|
||||
PubKeyBytesLenHybrid = 65
|
||||
)
|
||||
|
||||
func isOdd(a *big.Int) bool {
|
||||
return a.Bit(0) == 1
|
||||
}
|
||||
|
||||
// decompressPoint decompresses a point on the given curve given the X point and
|
||||
// the solution to use.
|
||||
func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) {
|
||||
// TODO: This will probably only work for secp256k1 due to
|
||||
// optimizations.
|
||||
|
||||
// Y = +-sqrt(x^3 + B)
|
||||
x3 := new(big.Int).Mul(x, x)
|
||||
x3.Mul(x3, x)
|
||||
x3.Add(x3, curve.Params().B)
|
||||
x3.Mod(x3, curve.Params().P)
|
||||
|
||||
// Now calculate sqrt mod p of x^3 + B
|
||||
// This code used to do a full sqrt based on tonelli/shanks,
|
||||
// but this was replaced by the algorithms referenced in
|
||||
// https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294
|
||||
y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P)
|
||||
|
||||
if ybit != isOdd(y) {
|
||||
y.Sub(curve.Params().P, y)
|
||||
}
|
||||
|
||||
// Check that y is a square root of x^3 + B.
|
||||
y2 := new(big.Int).Mul(y, y)
|
||||
y2.Mod(y2, curve.Params().P)
|
||||
if y2.Cmp(x3) != 0 {
|
||||
return nil, fmt.Errorf("invalid square root")
|
||||
}
|
||||
|
||||
// Verify that y-coord has expected parity.
|
||||
if ybit != isOdd(y) {
|
||||
return nil, fmt.Errorf("ybit doesn't match oddness")
|
||||
}
|
||||
|
||||
return y, nil
|
||||
}
|
||||
|
||||
const (
|
||||
pubkeyCompressed byte = 0x2 // y_bit + x coord
|
||||
pubkeyUncompressed byte = 0x4 // x coord + y coord
|
||||
pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord
|
||||
)
|
||||
|
||||
// IsCompressedPubKey returns true the the passed serialized public key has
|
||||
// been encoded in compressed format, and false otherwise.
|
||||
func IsCompressedPubKey(pubKey []byte) bool {
|
||||
// The public key is only compressed if it is the correct length and
|
||||
// the format (first byte) is one of the compressed pubkey values.
|
||||
return len(pubKey) == PubKeyBytesLenCompressed &&
|
||||
(pubKey[0]&^byte(0x1) == pubkeyCompressed)
|
||||
}
|
||||
|
||||
// ParsePubKey parses a public key for a koblitz curve from a bytestring into a
|
||||
// ecdsa.Publickey, verifying that it is valid. It supports compressed,
|
||||
// uncompressed and hybrid signature formats.
|
||||
func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) {
|
||||
pubkey := PublicKey{}
|
||||
pubkey.Curve = curve
|
||||
|
||||
if len(pubKeyStr) == 0 {
|
||||
return nil, errors.New("pubkey string is empty")
|
||||
}
|
||||
|
||||
format := pubKeyStr[0]
|
||||
ybit := (format & 0x1) == 0x1
|
||||
format &= ^byte(0x1)
|
||||
|
||||
switch len(pubKeyStr) {
|
||||
case PubKeyBytesLenUncompressed:
|
||||
if format != pubkeyUncompressed && format != pubkeyHybrid {
|
||||
return nil, fmt.Errorf("invalid magic in pubkey str: "+
|
||||
"%d", pubKeyStr[0])
|
||||
}
|
||||
|
||||
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
||||
pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:])
|
||||
// hybrid keys have extra information, make use of it.
|
||||
if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) {
|
||||
return nil, fmt.Errorf("ybit doesn't match oddness")
|
||||
}
|
||||
case PubKeyBytesLenCompressed:
|
||||
// format is 0x2 | solution, <X coordinate>
|
||||
// solution determines which solution of the curve we use.
|
||||
/// y^2 = x^3 + Curve.B
|
||||
if format != pubkeyCompressed {
|
||||
return nil, fmt.Errorf("invalid magic in compressed "+
|
||||
"pubkey string: %d", pubKeyStr[0])
|
||||
}
|
||||
pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33])
|
||||
pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default: // wrong!
|
||||
return nil, fmt.Errorf("invalid pub key length %d",
|
||||
len(pubKeyStr))
|
||||
}
|
||||
|
||||
if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 {
|
||||
return nil, fmt.Errorf("pubkey X parameter is >= to P")
|
||||
}
|
||||
if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 {
|
||||
return nil, fmt.Errorf("pubkey Y parameter is >= to P")
|
||||
}
|
||||
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
||||
return nil, fmt.Errorf("pubkey isn't on secp256k1 curve")
|
||||
}
|
||||
return &pubkey, nil
|
||||
}
|
||||
|
||||
// PublicKey is an ecdsa.PublicKey with additional functions to
|
||||
// serialize in uncompressed, compressed, and hybrid formats.
|
||||
type PublicKey ecdsa.PublicKey
|
||||
|
||||
// ToECDSA returns the public key as a *ecdsa.PublicKey.
|
||||
func (p *PublicKey) ToECDSA() *ecdsa.PublicKey {
|
||||
return (*ecdsa.PublicKey)(p)
|
||||
}
|
||||
|
||||
// SerializeUncompressed serializes a public key in a 65-byte uncompressed
|
||||
// format.
|
||||
func (p *PublicKey) SerializeUncompressed() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenUncompressed)
|
||||
b = append(b, pubkeyUncompressed)
|
||||
b = paddedAppend(32, b, p.X.Bytes())
|
||||
return paddedAppend(32, b, p.Y.Bytes())
|
||||
}
|
||||
|
||||
// SerializeCompressed serializes a public key in a 33-byte compressed format.
|
||||
func (p *PublicKey) SerializeCompressed() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenCompressed)
|
||||
format := pubkeyCompressed
|
||||
if isOdd(p.Y) {
|
||||
format |= 0x1
|
||||
}
|
||||
b = append(b, format)
|
||||
return paddedAppend(32, b, p.X.Bytes())
|
||||
}
|
||||
|
||||
// SerializeHybrid serializes a public key in a 65-byte hybrid format.
|
||||
func (p *PublicKey) SerializeHybrid() []byte {
|
||||
b := make([]byte, 0, PubKeyBytesLenHybrid)
|
||||
format := pubkeyHybrid
|
||||
if isOdd(p.Y) {
|
||||
format |= 0x1
|
||||
}
|
||||
b = append(b, format)
|
||||
b = paddedAppend(32, b, p.X.Bytes())
|
||||
return paddedAppend(32, b, p.Y.Bytes())
|
||||
}
|
||||
|
||||
// IsEqual compares this PublicKey instance to the one passed, returning true if
|
||||
// both PublicKeys are equivalent. A PublicKey is equivalent to another, if they
|
||||
// both have the same X and Y coordinate.
|
||||
func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool {
|
||||
return p.X.Cmp(otherPubKey.X) == 0 &&
|
||||
p.Y.Cmp(otherPubKey.Y) == 0
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,540 @@
|
|||
// Copyright (c) 2013-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Errors returned by canonicalPadding.
|
||||
var (
|
||||
errNegativeValue = errors.New("value may be interpreted as negative")
|
||||
errExcessivelyPaddedValue = errors.New("value is excessively padded")
|
||||
)
|
||||
|
||||
// Signature is a type representing an ecdsa signature.
|
||||
type Signature struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
// Used in RFC6979 implementation when testing the nonce for correctness
|
||||
one = big.NewInt(1)
|
||||
|
||||
// oneInitializer is used to fill a byte slice with byte 0x01. It is provided
|
||||
// here to avoid the need to create it multiple times.
|
||||
oneInitializer = []byte{0x01}
|
||||
)
|
||||
|
||||
// Serialize returns the ECDSA signature in the more strict DER format. Note
|
||||
// that the serialized bytes returned do not include the appended hash type
|
||||
// used in Bitcoin signature scripts.
|
||||
//
|
||||
// encoding/asn1 is broken so we hand roll this output:
|
||||
//
|
||||
// 0x30 <length> 0x02 <length r> r 0x02 <length s> s
|
||||
func (sig *Signature) Serialize() []byte {
|
||||
// low 'S' malleability breaker
|
||||
sigS := sig.S
|
||||
if sigS.Cmp(S256().halfOrder) == 1 {
|
||||
sigS = new(big.Int).Sub(S256().N, sigS)
|
||||
}
|
||||
// Ensure the encoded bytes for the r and s values are canonical and
|
||||
// thus suitable for DER encoding.
|
||||
rb := canonicalizeInt(sig.R)
|
||||
sb := canonicalizeInt(sigS)
|
||||
|
||||
// total length of returned signature is 1 byte for each magic and
|
||||
// length (6 total), plus lengths of r and s
|
||||
length := 6 + len(rb) + len(sb)
|
||||
b := make([]byte, length)
|
||||
|
||||
b[0] = 0x30
|
||||
b[1] = byte(length - 2)
|
||||
b[2] = 0x02
|
||||
b[3] = byte(len(rb))
|
||||
offset := copy(b[4:], rb) + 4
|
||||
b[offset] = 0x02
|
||||
b[offset+1] = byte(len(sb))
|
||||
copy(b[offset+2:], sb)
|
||||
return b
|
||||
}
|
||||
|
||||
// Verify calls ecdsa.Verify to verify the signature of hash using the public
|
||||
// key. It returns true if the signature is valid, false otherwise.
|
||||
func (sig *Signature) Verify(hash []byte, pubKey *PublicKey) bool {
|
||||
return ecdsa.Verify(pubKey.ToECDSA(), hash, sig.R, sig.S)
|
||||
}
|
||||
|
||||
// IsEqual compares this Signature instance to the one passed, returning true
|
||||
// if both Signatures are equivalent. A signature is equivalent to another, if
|
||||
// they both have the same scalar value for R and S.
|
||||
func (sig *Signature) IsEqual(otherSig *Signature) bool {
|
||||
return sig.R.Cmp(otherSig.R) == 0 &&
|
||||
sig.S.Cmp(otherSig.S) == 0
|
||||
}
|
||||
|
||||
// MinSigLen is the minimum length of a DER encoded signature and is when both R
|
||||
// and S are 1 byte each.
|
||||
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
|
||||
const MinSigLen = 8
|
||||
|
||||
func parseSig(sigStr []byte, curve elliptic.Curve, der bool) (*Signature, error) {
|
||||
// Originally this code used encoding/asn1 in order to parse the
|
||||
// signature, but a number of problems were found with this approach.
|
||||
// Despite the fact that signatures are stored as DER, the difference
|
||||
// between go's idea of a bignum (and that they have sign) doesn't agree
|
||||
// with the openssl one (where they do not). The above is true as of
|
||||
// Go 1.1. In the end it was simpler to rewrite the code to explicitly
|
||||
// understand the format which is this:
|
||||
// 0x30 <length of whole message> <0x02> <length of R> <R> 0x2
|
||||
// <length of S> <S>.
|
||||
|
||||
signature := &Signature{}
|
||||
|
||||
if len(sigStr) < MinSigLen {
|
||||
return nil, errors.New("malformed signature: too short")
|
||||
}
|
||||
// 0x30
|
||||
index := 0
|
||||
if sigStr[index] != 0x30 {
|
||||
return nil, errors.New("malformed signature: no header magic")
|
||||
}
|
||||
index++
|
||||
// length of remaining message
|
||||
siglen := sigStr[index]
|
||||
index++
|
||||
|
||||
// siglen should be less than the entire message and greater than
|
||||
// the minimal message size.
|
||||
if int(siglen+2) > len(sigStr) || int(siglen+2) < MinSigLen {
|
||||
return nil, errors.New("malformed signature: bad length")
|
||||
}
|
||||
// trim the slice we're working on so we only look at what matters.
|
||||
sigStr = sigStr[:siglen+2]
|
||||
|
||||
// 0x02
|
||||
if sigStr[index] != 0x02 {
|
||||
return nil,
|
||||
errors.New("malformed signature: no 1st int marker")
|
||||
}
|
||||
index++
|
||||
|
||||
// Length of signature R.
|
||||
rLen := int(sigStr[index])
|
||||
// must be positive, must be able to fit in another 0x2, <len> <s>
|
||||
// hence the -3. We assume that the length must be at least one byte.
|
||||
index++
|
||||
if rLen <= 0 || rLen > len(sigStr)-index-3 {
|
||||
return nil, errors.New("malformed signature: bogus R length")
|
||||
}
|
||||
|
||||
// Then R itself.
|
||||
rBytes := sigStr[index : index+rLen]
|
||||
if der {
|
||||
switch err := canonicalPadding(rBytes); err {
|
||||
case errNegativeValue:
|
||||
return nil, errors.New("signature R is negative")
|
||||
case errExcessivelyPaddedValue:
|
||||
return nil, errors.New("signature R is excessively padded")
|
||||
}
|
||||
}
|
||||
signature.R = new(big.Int).SetBytes(rBytes)
|
||||
index += rLen
|
||||
// 0x02. length already checked in previous if.
|
||||
if sigStr[index] != 0x02 {
|
||||
return nil, errors.New("malformed signature: no 2nd int marker")
|
||||
}
|
||||
index++
|
||||
|
||||
// Length of signature S.
|
||||
sLen := int(sigStr[index])
|
||||
index++
|
||||
// S should be the rest of the string.
|
||||
if sLen <= 0 || sLen > len(sigStr)-index {
|
||||
return nil, errors.New("malformed signature: bogus S length")
|
||||
}
|
||||
|
||||
// Then S itself.
|
||||
sBytes := sigStr[index : index+sLen]
|
||||
if der {
|
||||
switch err := canonicalPadding(sBytes); err {
|
||||
case errNegativeValue:
|
||||
return nil, errors.New("signature S is negative")
|
||||
case errExcessivelyPaddedValue:
|
||||
return nil, errors.New("signature S is excessively padded")
|
||||
}
|
||||
}
|
||||
signature.S = new(big.Int).SetBytes(sBytes)
|
||||
index += sLen
|
||||
|
||||
// sanity check length parsing
|
||||
if index != len(sigStr) {
|
||||
return nil, fmt.Errorf("malformed signature: bad final length %v != %v",
|
||||
index, len(sigStr))
|
||||
}
|
||||
|
||||
// Verify also checks this, but we can be more sure that we parsed
|
||||
// correctly if we verify here too.
|
||||
// FWIW the ecdsa spec states that R and S must be | 1, N - 1 |
|
||||
// but crypto/ecdsa only checks for Sign != 0. Mirror that.
|
||||
if signature.R.Sign() != 1 {
|
||||
return nil, errors.New("signature R isn't 1 or more")
|
||||
}
|
||||
if signature.S.Sign() != 1 {
|
||||
return nil, errors.New("signature S isn't 1 or more")
|
||||
}
|
||||
if signature.R.Cmp(curve.Params().N) >= 0 {
|
||||
return nil, errors.New("signature R is >= curve.N")
|
||||
}
|
||||
if signature.S.Cmp(curve.Params().N) >= 0 {
|
||||
return nil, errors.New("signature S is >= curve.N")
|
||||
}
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// ParseSignature parses a signature in BER format for the curve type `curve'
|
||||
// into a Signature type, perfoming some basic sanity checks. If parsing
|
||||
// according to the more strict DER format is needed, use ParseDERSignature.
|
||||
func ParseSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) {
|
||||
return parseSig(sigStr, curve, false)
|
||||
}
|
||||
|
||||
// ParseDERSignature parses a signature in DER format for the curve type
|
||||
// `curve` into a Signature type. If parsing according to the less strict
|
||||
// BER format is needed, use ParseSignature.
|
||||
func ParseDERSignature(sigStr []byte, curve elliptic.Curve) (*Signature, error) {
|
||||
return parseSig(sigStr, curve, true)
|
||||
}
|
||||
|
||||
// canonicalizeInt returns the bytes for the passed big integer adjusted as
|
||||
// necessary to ensure that a big-endian encoded integer can't possibly be
|
||||
// misinterpreted as a negative number. This can happen when the most
|
||||
// significant bit is set, so it is padded by a leading zero byte in this case.
|
||||
// Also, the returned bytes will have at least a single byte when the passed
|
||||
// value is 0. This is required for DER encoding.
|
||||
func canonicalizeInt(val *big.Int) []byte {
|
||||
b := val.Bytes()
|
||||
if len(b) == 0 {
|
||||
b = []byte{0x00}
|
||||
}
|
||||
if b[0]&0x80 != 0 {
|
||||
paddedBytes := make([]byte, len(b)+1)
|
||||
copy(paddedBytes[1:], b)
|
||||
b = paddedBytes
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// canonicalPadding checks whether a big-endian encoded integer could
|
||||
// possibly be misinterpreted as a negative number (even though OpenSSL
|
||||
// treats all numbers as unsigned), or if there is any unnecessary
|
||||
// leading zero padding.
|
||||
func canonicalPadding(b []byte) error {
|
||||
switch {
|
||||
case b[0]&0x80 == 0x80:
|
||||
return errNegativeValue
|
||||
case len(b) > 1 && b[0] == 0x00 && b[1]&0x80 != 0x80:
|
||||
return errExcessivelyPaddedValue
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// hashToInt converts a hash value to an integer. There is some disagreement
|
||||
// about how this is done. [NSA] suggests that this is done in the obvious
|
||||
// manner, but [SECG] truncates the hash to the bit-length of the curve order
|
||||
// first. We follow [SECG] because that's what OpenSSL does. Additionally,
|
||||
// OpenSSL right shifts excess bits from the number if the hash is too large
|
||||
// and we mirror that too.
|
||||
// This is borrowed from crypto/ecdsa.
|
||||
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
|
||||
orderBits := c.Params().N.BitLen()
|
||||
orderBytes := (orderBits + 7) / 8
|
||||
if len(hash) > orderBytes {
|
||||
hash = hash[:orderBytes]
|
||||
}
|
||||
|
||||
ret := new(big.Int).SetBytes(hash)
|
||||
excess := len(hash)*8 - orderBits
|
||||
if excess > 0 {
|
||||
ret.Rsh(ret, uint(excess))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// recoverKeyFromSignature recovers a public key from the signature "sig" on the
|
||||
// given message hash "msg". Based on the algorithm found in section 5.1.5 of
|
||||
// SEC 1 Ver 2.0, page 47-48 (53 and 54 in the pdf). This performs the details
|
||||
// in the inner loop in Step 1. The counter provided is actually the j parameter
|
||||
// of the loop * 2 - on the first iteration of j we do the R case, else the -R
|
||||
// case in step 1.6. This counter is used in the bitcoin compressed signature
|
||||
// format and thus we match bitcoind's behaviour here.
|
||||
func recoverKeyFromSignature(curve *KoblitzCurve, sig *Signature, msg []byte,
|
||||
iter int, doChecks bool) (*PublicKey, error) {
|
||||
// 1.1 x = (n * i) + r
|
||||
Rx := new(big.Int).Mul(curve.Params().N,
|
||||
new(big.Int).SetInt64(int64(iter/2)))
|
||||
Rx.Add(Rx, sig.R)
|
||||
if Rx.Cmp(curve.Params().P) != -1 {
|
||||
return nil, errors.New("calculated Rx is larger than curve P")
|
||||
}
|
||||
|
||||
// convert 02<Rx> to point R. (step 1.2 and 1.3). If we are on an odd
|
||||
// iteration then 1.6 will be done with -R, so we calculate the other
|
||||
// term when uncompressing the point.
|
||||
Ry, err := decompressPoint(curve, Rx, iter%2 == 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1.4 Check n*R is point at infinity
|
||||
if doChecks {
|
||||
nRx, nRy := curve.ScalarMult(Rx, Ry, curve.Params().N.Bytes())
|
||||
if nRx.Sign() != 0 || nRy.Sign() != 0 {
|
||||
return nil, errors.New("n*R does not equal the point at infinity")
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 calculate e from message using the same algorithm as ecdsa
|
||||
// signature calculation.
|
||||
e := hashToInt(msg, curve)
|
||||
|
||||
// Step 1.6.1:
|
||||
// We calculate the two terms sR and eG separately multiplied by the
|
||||
// inverse of r (from the signature). We then add them to calculate
|
||||
// Q = r^-1(sR-eG)
|
||||
invr := new(big.Int).ModInverse(sig.R, curve.Params().N)
|
||||
|
||||
// first term.
|
||||
invrS := new(big.Int).Mul(invr, sig.S)
|
||||
invrS.Mod(invrS, curve.Params().N)
|
||||
sRx, sRy := curve.ScalarMult(Rx, Ry, invrS.Bytes())
|
||||
|
||||
// second term.
|
||||
e.Neg(e)
|
||||
e.Mod(e, curve.Params().N)
|
||||
e.Mul(e, invr)
|
||||
e.Mod(e, curve.Params().N)
|
||||
minuseGx, minuseGy := curve.ScalarBaseMult(e.Bytes())
|
||||
|
||||
// TODO: this would be faster if we did a mult and add in one
|
||||
// step to prevent the jacobian conversion back and forth.
|
||||
Qx, Qy := curve.Add(sRx, sRy, minuseGx, minuseGy)
|
||||
|
||||
return &PublicKey{
|
||||
Curve: curve,
|
||||
X: Qx,
|
||||
Y: Qy,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SignCompact produces a compact signature of the data in hash with the given
|
||||
// private key on the given koblitz curve. The isCompressed parameter should
|
||||
// be used to detail if the given signature should reference a compressed
|
||||
// public key or not. If successful the bytes of the compact signature will be
|
||||
// returned in the format:
|
||||
// <(byte of 27+public key solution)+4 if compressed >< padded bytes for signature R><padded bytes for signature S>
|
||||
// where the R and S parameters are padde up to the bitlengh of the curve.
|
||||
func SignCompact(curve *KoblitzCurve, key *PrivateKey,
|
||||
hash []byte, isCompressedKey bool) ([]byte, error) {
|
||||
sig, err := key.Sign(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// bitcoind checks the bit length of R and S here. The ecdsa signature
|
||||
// algorithm returns R and S mod N therefore they will be the bitsize of
|
||||
// the curve, and thus correctly sized.
|
||||
for i := 0; i < (curve.H+1)*2; i++ {
|
||||
pk, err := recoverKeyFromSignature(curve, sig, hash, i, true)
|
||||
if err == nil && pk.X.Cmp(key.X) == 0 && pk.Y.Cmp(key.Y) == 0 {
|
||||
result := make([]byte, 1, 2*curve.byteSize+1)
|
||||
result[0] = 27 + byte(i)
|
||||
if isCompressedKey {
|
||||
result[0] += 4
|
||||
}
|
||||
// Not sure this needs rounding but safer to do so.
|
||||
curvelen := (curve.BitSize + 7) / 8
|
||||
|
||||
// Pad R and S to curvelen if needed.
|
||||
bytelen := (sig.R.BitLen() + 7) / 8
|
||||
if bytelen < curvelen {
|
||||
result = append(result,
|
||||
make([]byte, curvelen-bytelen)...)
|
||||
}
|
||||
result = append(result, sig.R.Bytes()...)
|
||||
|
||||
bytelen = (sig.S.BitLen() + 7) / 8
|
||||
if bytelen < curvelen {
|
||||
result = append(result,
|
||||
make([]byte, curvelen-bytelen)...)
|
||||
}
|
||||
result = append(result, sig.S.Bytes()...)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("no valid solution for pubkey found")
|
||||
}
|
||||
|
||||
// RecoverCompact verifies the compact signature "signature" of "hash" for the
|
||||
// Koblitz curve in "curve". If the signature matches then the recovered public
|
||||
// key will be returned as well as a boolen if the original key was compressed
|
||||
// or not, else an error will be returned.
|
||||
func RecoverCompact(curve *KoblitzCurve, signature,
|
||||
hash []byte) (*PublicKey, bool, error) {
|
||||
bitlen := (curve.BitSize + 7) / 8
|
||||
if len(signature) != 1+bitlen*2 {
|
||||
return nil, false, errors.New("invalid compact signature size")
|
||||
}
|
||||
|
||||
iteration := int((signature[0] - 27) & ^byte(4))
|
||||
|
||||
// format is <header byte><bitlen R><bitlen S>
|
||||
sig := &Signature{
|
||||
R: new(big.Int).SetBytes(signature[1 : bitlen+1]),
|
||||
S: new(big.Int).SetBytes(signature[bitlen+1:]),
|
||||
}
|
||||
// The iteration used here was encoded
|
||||
key, err := recoverKeyFromSignature(curve, sig, hash, iteration, false)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return key, ((signature[0] - 27) & 4) == 4, nil
|
||||
}
|
||||
|
||||
// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979 and BIP 62.
|
||||
func signRFC6979(privateKey *PrivateKey, hash []byte) (*Signature, error) {
|
||||
|
||||
privkey := privateKey.ToECDSA()
|
||||
N := S256().N
|
||||
halfOrder := S256().halfOrder
|
||||
k := nonceRFC6979(privkey.D, hash)
|
||||
inv := new(big.Int).ModInverse(k, N)
|
||||
r, _ := privkey.Curve.ScalarBaseMult(k.Bytes())
|
||||
r.Mod(r, N)
|
||||
|
||||
if r.Sign() == 0 {
|
||||
return nil, errors.New("calculated R is zero")
|
||||
}
|
||||
|
||||
e := hashToInt(hash, privkey.Curve)
|
||||
s := new(big.Int).Mul(privkey.D, r)
|
||||
s.Add(s, e)
|
||||
s.Mul(s, inv)
|
||||
s.Mod(s, N)
|
||||
|
||||
if s.Cmp(halfOrder) == 1 {
|
||||
s.Sub(N, s)
|
||||
}
|
||||
if s.Sign() == 0 {
|
||||
return nil, errors.New("calculated S is zero")
|
||||
}
|
||||
return &Signature{R: r, S: s}, nil
|
||||
}
|
||||
|
||||
// nonceRFC6979 generates an ECDSA nonce (`k`) deterministically according to RFC 6979.
|
||||
// It takes a 32-byte hash as an input and returns 32-byte nonce to be used in ECDSA algorithm.
|
||||
func nonceRFC6979(privkey *big.Int, hash []byte) *big.Int {
|
||||
|
||||
curve := S256()
|
||||
q := curve.Params().N
|
||||
x := privkey
|
||||
alg := sha256.New
|
||||
|
||||
qlen := q.BitLen()
|
||||
holen := alg().Size()
|
||||
rolen := (qlen + 7) >> 3
|
||||
bx := append(int2octets(x, rolen), bits2octets(hash, curve, rolen)...)
|
||||
|
||||
// Step B
|
||||
v := bytes.Repeat(oneInitializer, holen)
|
||||
|
||||
// Step C (Go zeroes the all allocated memory)
|
||||
k := make([]byte, holen)
|
||||
|
||||
// Step D
|
||||
k = mac(alg, k, append(append(v, 0x00), bx...))
|
||||
|
||||
// Step E
|
||||
v = mac(alg, k, v)
|
||||
|
||||
// Step F
|
||||
k = mac(alg, k, append(append(v, 0x01), bx...))
|
||||
|
||||
// Step G
|
||||
v = mac(alg, k, v)
|
||||
|
||||
// Step H
|
||||
for {
|
||||
// Step H1
|
||||
var t []byte
|
||||
|
||||
// Step H2
|
||||
for len(t)*8 < qlen {
|
||||
v = mac(alg, k, v)
|
||||
t = append(t, v...)
|
||||
}
|
||||
|
||||
// Step H3
|
||||
secret := hashToInt(t, curve)
|
||||
if secret.Cmp(one) >= 0 && secret.Cmp(q) < 0 {
|
||||
return secret
|
||||
}
|
||||
k = mac(alg, k, append(v, 0x00))
|
||||
v = mac(alg, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// mac returns an HMAC of the given key and message.
|
||||
func mac(alg func() hash.Hash, k, m []byte) []byte {
|
||||
h := hmac.New(alg, k)
|
||||
h.Write(m)
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc6979#section-2.3.3
|
||||
func int2octets(v *big.Int, rolen int) []byte {
|
||||
out := v.Bytes()
|
||||
|
||||
// left pad with zeros if it's too short
|
||||
if len(out) < rolen {
|
||||
out2 := make([]byte, rolen)
|
||||
copy(out2[rolen-len(out):], out)
|
||||
return out2
|
||||
}
|
||||
|
||||
// drop most significant bytes if it's too long
|
||||
if len(out) > rolen {
|
||||
out2 := make([]byte, rolen)
|
||||
copy(out2, out[len(out)-rolen:])
|
||||
return out2
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc6979#section-2.3.4
|
||||
func bits2octets(in []byte, curve elliptic.Curve, rolen int) []byte {
|
||||
z1 := hashToInt(in, curve)
|
||||
z2 := new(big.Int).Sub(z1, curve.Params().N)
|
||||
if z2.Sign() < 0 {
|
||||
return int2octets(z1, rolen)
|
||||
}
|
||||
return int2octets(z2, rolen)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
# This is the list of people who have contributed code to the repository.
|
||||
#
|
||||
# Names should be added to this file only after verifying that the individual
|
||||
# or the individual's organization has agreed to the LICENSE.
|
||||
#
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
John C. Vernaleo <jcv@conformal.com>
|
||||
Dave Collins <davec@conformal.com>
|
||||
Owain G. Ainsworth <oga@conformal.com>
|
||||
David Hill <dhill@conformal.com>
|
||||
Josh Rickmar <jrick@conformal.com>
|
||||
Andreas Metsälä <andreas.metsala@gmail.com>
|
||||
Francis Lam <flam@alum.mit.edu>
|
||||
Geert-Johan Riemer <geertjohan.riemer@gmail.com>
|
|
@ -0,0 +1,70 @@
|
|||
btcjson
|
||||
=======
|
||||
|
||||
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcd)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/btcjson)
|
||||
|
||||
Package btcjson implements concrete types for marshalling to and from the
|
||||
bitcoin JSON-RPC API. A comprehensive suite of tests is provided to ensure
|
||||
proper functionality.
|
||||
|
||||
Although this package was primarily written for the btcsuite, it has
|
||||
intentionally been designed so it can be used as a standalone package for any
|
||||
projects needing to marshal to and from bitcoin JSON-RPC requests and responses.
|
||||
|
||||
Note that although it's possible to use this package directly to implement an
|
||||
RPC client, it is not recommended since it is only intended as an infrastructure
|
||||
package. Instead, RPC clients should use the
|
||||
[btcrpcclient](https://github.com/btcsuite/btcrpcclient) package which provides
|
||||
a full blown RPC client with many features such as automatic connection
|
||||
management, websocket support, automatic notification re-registration on
|
||||
reconnect, and conversion from the raw underlying RPC types (strings, floats,
|
||||
ints, etc) to higher-level types with many nice and useful properties.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/btcjson
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [Marshal Command](http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalCmd)
|
||||
Demonstrates how to create and marshal a command into a JSON-RPC request.
|
||||
|
||||
* [Unmarshal Command](http://godoc.org/github.com/btcsuite/btcd/btcjson#example-UnmarshalCmd)
|
||||
Demonstrates how to unmarshal a JSON-RPC request and then unmarshal the
|
||||
concrete request into a concrete command.
|
||||
|
||||
* [Marshal Response](http://godoc.org/github.com/btcsuite/btcd/btcjson#example-MarshalResponse)
|
||||
Demonstrates how to marshal a JSON-RPC response.
|
||||
|
||||
* [Unmarshal Response](http://godoc.org/github.com/btcsuite/btcd/btcjson#example-package--UnmarshalResponse)
|
||||
Demonstrates how to unmarshal a JSON-RPC response and then unmarshal the
|
||||
result field in the response to a concrete type.
|
||||
|
||||
## GPG Verification Key
|
||||
|
||||
All official release tags are signed by Conformal so users can ensure the code
|
||||
has not been tampered with and is coming from the btcsuite developers. To
|
||||
verify the signature perform the following:
|
||||
|
||||
- Download the public key from the Conformal website at
|
||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
||||
|
||||
- Import the public key into your GPG keyring:
|
||||
```bash
|
||||
gpg --import GIT-GPG-KEY-conformal.txt
|
||||
```
|
||||
|
||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
||||
placeholder for the specific tag:
|
||||
```bash
|
||||
git tag -v TAG_NAME
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package btcjson is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
|
@ -0,0 +1,138 @@
|
|||
// 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.
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a chain server with btcd extensions.
|
||||
|
||||
package btcjson
|
||||
|
||||
// NodeSubCmd defines the type used in the addnode JSON-RPC command for the
|
||||
// sub command field.
|
||||
type NodeSubCmd string
|
||||
|
||||
const (
|
||||
// NConnect indicates the specified host that should be connected to.
|
||||
NConnect NodeSubCmd = "connect"
|
||||
|
||||
// NRemove indicates the specified peer that should be removed as a
|
||||
// persistent peer.
|
||||
NRemove NodeSubCmd = "remove"
|
||||
|
||||
// NDisconnect indicates the specified peer should be disonnected.
|
||||
NDisconnect NodeSubCmd = "disconnect"
|
||||
)
|
||||
|
||||
// NodeCmd defines the dropnode JSON-RPC command.
|
||||
type NodeCmd struct {
|
||||
SubCmd NodeSubCmd `jsonrpcusage:"\"connect|remove|disconnect\""`
|
||||
Target string
|
||||
ConnectSubCmd *string `jsonrpcusage:"\"perm|temp\""`
|
||||
}
|
||||
|
||||
// NewNodeCmd returns a new instance which can be used to issue a `node`
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewNodeCmd(subCmd NodeSubCmd, target string, connectSubCmd *string) *NodeCmd {
|
||||
return &NodeCmd{
|
||||
SubCmd: subCmd,
|
||||
Target: target,
|
||||
ConnectSubCmd: connectSubCmd,
|
||||
}
|
||||
}
|
||||
|
||||
// DebugLevelCmd defines the debuglevel JSON-RPC command. This command is not a
|
||||
// standard Bitcoin command. It is an extension for btcd.
|
||||
type DebugLevelCmd struct {
|
||||
LevelSpec string
|
||||
}
|
||||
|
||||
// NewDebugLevelCmd returns a new DebugLevelCmd which can be used to issue a
|
||||
// debuglevel JSON-RPC command. This command is not a standard Bitcoin command.
|
||||
// It is an extension for btcd.
|
||||
func NewDebugLevelCmd(levelSpec string) *DebugLevelCmd {
|
||||
return &DebugLevelCmd{
|
||||
LevelSpec: levelSpec,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateCmd defines the generate JSON-RPC command.
|
||||
type GenerateCmd struct {
|
||||
NumBlocks uint32
|
||||
}
|
||||
|
||||
// NewGenerateCmd returns a new instance which can be used to issue a generate
|
||||
// JSON-RPC command.
|
||||
func NewGenerateCmd(numBlocks uint32) *GenerateCmd {
|
||||
return &GenerateCmd{
|
||||
NumBlocks: numBlocks,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBestBlockCmd defines the getbestblock JSON-RPC command.
|
||||
type GetBestBlockCmd struct{}
|
||||
|
||||
// NewGetBestBlockCmd returns a new instance which can be used to issue a
|
||||
// getbestblock JSON-RPC command.
|
||||
func NewGetBestBlockCmd() *GetBestBlockCmd {
|
||||
return &GetBestBlockCmd{}
|
||||
}
|
||||
|
||||
// GetCurrentNetCmd defines the getcurrentnet JSON-RPC command.
|
||||
type GetCurrentNetCmd struct{}
|
||||
|
||||
// NewGetCurrentNetCmd returns a new instance which can be used to issue a
|
||||
// getcurrentnet JSON-RPC command.
|
||||
func NewGetCurrentNetCmd() *GetCurrentNetCmd {
|
||||
return &GetCurrentNetCmd{}
|
||||
}
|
||||
|
||||
// GetHeadersCmd defines the getheaders JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
type GetHeadersCmd struct {
|
||||
BlockLocators []string `json:"blocklocators"`
|
||||
HashStop string `json:"hashstop"`
|
||||
}
|
||||
|
||||
// NewGetHeadersCmd returns a new instance which can be used to issue a
|
||||
// getheaders JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
func NewGetHeadersCmd(blockLocators []string, hashStop string) *GetHeadersCmd {
|
||||
return &GetHeadersCmd{
|
||||
BlockLocators: blockLocators,
|
||||
HashStop: hashStop,
|
||||
}
|
||||
}
|
||||
|
||||
// VersionCmd defines the version JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
type VersionCmd struct{}
|
||||
|
||||
// NewVersionCmd returns a new instance which can be used to issue a JSON-RPC
|
||||
// version command.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
func NewVersionCmd() *VersionCmd { return new(VersionCmd) }
|
||||
|
||||
func init() {
|
||||
// No special flags for commands in this file.
|
||||
flags := UsageFlag(0)
|
||||
|
||||
MustRegisterCmd("debuglevel", (*DebugLevelCmd)(nil), flags)
|
||||
MustRegisterCmd("node", (*NodeCmd)(nil), flags)
|
||||
MustRegisterCmd("generate", (*GenerateCmd)(nil), flags)
|
||||
MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags)
|
||||
MustRegisterCmd("getheaders", (*GetHeadersCmd)(nil), flags)
|
||||
MustRegisterCmd("version", (*VersionCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) 2016-2017 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 btcjson
|
||||
|
||||
// VersionResult models objects included in the version response. In the actual
|
||||
// result, these objects are keyed by the program or API name.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
type VersionResult struct {
|
||||
VersionString string `json:"versionstring"`
|
||||
Major uint32 `json:"major"`
|
||||
Minor uint32 `json:"minor"`
|
||||
Patch uint32 `json:"patch"`
|
||||
Prerelease string `json:"prerelease"`
|
||||
BuildMetadata string `json:"buildmetadata"`
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a wallet server with btcwallet extensions.
|
||||
|
||||
package btcjson
|
||||
|
||||
// CreateNewAccountCmd defines the createnewaccount JSON-RPC command.
|
||||
type CreateNewAccountCmd struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
// NewCreateNewAccountCmd returns a new instance which can be used to issue a
|
||||
// createnewaccount JSON-RPC command.
|
||||
func NewCreateNewAccountCmd(account string) *CreateNewAccountCmd {
|
||||
return &CreateNewAccountCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// DumpWalletCmd defines the dumpwallet JSON-RPC command.
|
||||
type DumpWalletCmd struct {
|
||||
Filename string
|
||||
}
|
||||
|
||||
// NewDumpWalletCmd returns a new instance which can be used to issue a
|
||||
// dumpwallet JSON-RPC command.
|
||||
func NewDumpWalletCmd(filename string) *DumpWalletCmd {
|
||||
return &DumpWalletCmd{
|
||||
Filename: filename,
|
||||
}
|
||||
}
|
||||
|
||||
// ImportAddressCmd defines the importaddress JSON-RPC command.
|
||||
type ImportAddressCmd struct {
|
||||
Address string
|
||||
Account string
|
||||
Rescan *bool `jsonrpcdefault:"true"`
|
||||
}
|
||||
|
||||
// NewImportAddressCmd returns a new instance which can be used to issue an
|
||||
// importaddress JSON-RPC command.
|
||||
func NewImportAddressCmd(address string, account string, rescan *bool) *ImportAddressCmd {
|
||||
return &ImportAddressCmd{
|
||||
Address: address,
|
||||
Account: account,
|
||||
Rescan: rescan,
|
||||
}
|
||||
}
|
||||
|
||||
// ImportPubKeyCmd defines the importpubkey JSON-RPC command.
|
||||
type ImportPubKeyCmd struct {
|
||||
PubKey string
|
||||
Rescan *bool `jsonrpcdefault:"true"`
|
||||
}
|
||||
|
||||
// NewImportPubKeyCmd returns a new instance which can be used to issue an
|
||||
// importpubkey JSON-RPC command.
|
||||
func NewImportPubKeyCmd(pubKey string, rescan *bool) *ImportPubKeyCmd {
|
||||
return &ImportPubKeyCmd{
|
||||
PubKey: pubKey,
|
||||
Rescan: rescan,
|
||||
}
|
||||
}
|
||||
|
||||
// ImportWalletCmd defines the importwallet JSON-RPC command.
|
||||
type ImportWalletCmd struct {
|
||||
Filename string
|
||||
}
|
||||
|
||||
// NewImportWalletCmd returns a new instance which can be used to issue a
|
||||
// importwallet JSON-RPC command.
|
||||
func NewImportWalletCmd(filename string) *ImportWalletCmd {
|
||||
return &ImportWalletCmd{
|
||||
Filename: filename,
|
||||
}
|
||||
}
|
||||
|
||||
// RenameAccountCmd defines the renameaccount JSON-RPC command.
|
||||
type RenameAccountCmd struct {
|
||||
OldAccount string
|
||||
NewAccount string
|
||||
}
|
||||
|
||||
// NewRenameAccountCmd returns a new instance which can be used to issue a
|
||||
// renameaccount JSON-RPC command.
|
||||
func NewRenameAccountCmd(oldAccount, newAccount string) *RenameAccountCmd {
|
||||
return &RenameAccountCmd{
|
||||
OldAccount: oldAccount,
|
||||
NewAccount: newAccount,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable with a wallet server.
|
||||
flags := UFWalletOnly
|
||||
|
||||
MustRegisterCmd("createnewaccount", (*CreateNewAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("dumpwallet", (*DumpWalletCmd)(nil), flags)
|
||||
MustRegisterCmd("importaddress", (*ImportAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("importpubkey", (*ImportPubKeyCmd)(nil), flags)
|
||||
MustRegisterCmd("importwallet", (*ImportWalletCmd)(nil), flags)
|
||||
MustRegisterCmd("renameaccount", (*RenameAccountCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,828 @@
|
|||
// Copyright (c) 2014-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a chain server.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// AddNodeSubCmd defines the type used in the addnode JSON-RPC command for the
|
||||
// sub command field.
|
||||
type AddNodeSubCmd string
|
||||
|
||||
const (
|
||||
// ANAdd indicates the specified host should be added as a persistent
|
||||
// peer.
|
||||
ANAdd AddNodeSubCmd = "add"
|
||||
|
||||
// ANRemove indicates the specified peer should be removed.
|
||||
ANRemove AddNodeSubCmd = "remove"
|
||||
|
||||
// ANOneTry indicates the specified host should try to connect once,
|
||||
// but it should not be made persistent.
|
||||
ANOneTry AddNodeSubCmd = "onetry"
|
||||
)
|
||||
|
||||
// AddNodeCmd defines the addnode JSON-RPC command.
|
||||
type AddNodeCmd struct {
|
||||
Addr string
|
||||
SubCmd AddNodeSubCmd `jsonrpcusage:"\"add|remove|onetry\""`
|
||||
}
|
||||
|
||||
// NewAddNodeCmd returns a new instance which can be used to issue an addnode
|
||||
// JSON-RPC command.
|
||||
func NewAddNodeCmd(addr string, subCmd AddNodeSubCmd) *AddNodeCmd {
|
||||
return &AddNodeCmd{
|
||||
Addr: addr,
|
||||
SubCmd: subCmd,
|
||||
}
|
||||
}
|
||||
|
||||
// TransactionInput represents the inputs to a transaction. Specifically a
|
||||
// transaction hash and output number pair.
|
||||
type TransactionInput struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
}
|
||||
|
||||
// CreateRawTransactionCmd defines the createrawtransaction JSON-RPC command.
|
||||
type CreateRawTransactionCmd struct {
|
||||
Inputs []TransactionInput
|
||||
Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC
|
||||
LockTime *int64
|
||||
}
|
||||
|
||||
// NewCreateRawTransactionCmd returns a new instance which can be used to issue
|
||||
// a createrawtransaction JSON-RPC command.
|
||||
//
|
||||
// Amounts are in BTC.
|
||||
func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64,
|
||||
lockTime *int64) *CreateRawTransactionCmd {
|
||||
|
||||
return &CreateRawTransactionCmd{
|
||||
Inputs: inputs,
|
||||
Amounts: amounts,
|
||||
LockTime: lockTime,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command.
|
||||
type DecodeRawTransactionCmd struct {
|
||||
HexTx string
|
||||
}
|
||||
|
||||
// NewDecodeRawTransactionCmd returns a new instance which can be used to issue
|
||||
// a decoderawtransaction JSON-RPC command.
|
||||
func NewDecodeRawTransactionCmd(hexTx string) *DecodeRawTransactionCmd {
|
||||
return &DecodeRawTransactionCmd{
|
||||
HexTx: hexTx,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeScriptCmd defines the decodescript JSON-RPC command.
|
||||
type DecodeScriptCmd struct {
|
||||
HexScript string
|
||||
}
|
||||
|
||||
// NewDecodeScriptCmd returns a new instance which can be used to issue a
|
||||
// decodescript JSON-RPC command.
|
||||
func NewDecodeScriptCmd(hexScript string) *DecodeScriptCmd {
|
||||
return &DecodeScriptCmd{
|
||||
HexScript: hexScript,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAddedNodeInfoCmd defines the getaddednodeinfo JSON-RPC command.
|
||||
type GetAddedNodeInfoCmd struct {
|
||||
DNS bool
|
||||
Node *string
|
||||
}
|
||||
|
||||
// NewGetAddedNodeInfoCmd returns a new instance which can be used to issue a
|
||||
// getaddednodeinfo JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetAddedNodeInfoCmd(dns bool, node *string) *GetAddedNodeInfoCmd {
|
||||
return &GetAddedNodeInfoCmd{
|
||||
DNS: dns,
|
||||
Node: node,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBestBlockHashCmd defines the getbestblockhash JSON-RPC command.
|
||||
type GetBestBlockHashCmd struct{}
|
||||
|
||||
// NewGetBestBlockHashCmd returns a new instance which can be used to issue a
|
||||
// getbestblockhash JSON-RPC command.
|
||||
func NewGetBestBlockHashCmd() *GetBestBlockHashCmd {
|
||||
return &GetBestBlockHashCmd{}
|
||||
}
|
||||
|
||||
// GetBlockCmd defines the getblock JSON-RPC command.
|
||||
type GetBlockCmd struct {
|
||||
Hash string
|
||||
Verbose *bool `jsonrpcdefault:"true"`
|
||||
VerboseTx *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewGetBlockCmd returns a new instance which can be used to issue a getblock
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetBlockCmd(hash string, verbose, verboseTx *bool) *GetBlockCmd {
|
||||
return &GetBlockCmd{
|
||||
Hash: hash,
|
||||
Verbose: verbose,
|
||||
VerboseTx: verboseTx,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBlockChainInfoCmd defines the getblockchaininfo JSON-RPC command.
|
||||
type GetBlockChainInfoCmd struct{}
|
||||
|
||||
// NewGetBlockChainInfoCmd returns a new instance which can be used to issue a
|
||||
// getblockchaininfo JSON-RPC command.
|
||||
func NewGetBlockChainInfoCmd() *GetBlockChainInfoCmd {
|
||||
return &GetBlockChainInfoCmd{}
|
||||
}
|
||||
|
||||
// GetBlockCountCmd defines the getblockcount JSON-RPC command.
|
||||
type GetBlockCountCmd struct{}
|
||||
|
||||
// NewGetBlockCountCmd returns a new instance which can be used to issue a
|
||||
// getblockcount JSON-RPC command.
|
||||
func NewGetBlockCountCmd() *GetBlockCountCmd {
|
||||
return &GetBlockCountCmd{}
|
||||
}
|
||||
|
||||
// GetBlockHashCmd defines the getblockhash JSON-RPC command.
|
||||
type GetBlockHashCmd struct {
|
||||
Index int64
|
||||
}
|
||||
|
||||
// NewGetBlockHashCmd returns a new instance which can be used to issue a
|
||||
// getblockhash JSON-RPC command.
|
||||
func NewGetBlockHashCmd(index int64) *GetBlockHashCmd {
|
||||
return &GetBlockHashCmd{
|
||||
Index: index,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBlockHeaderCmd defines the getblockheader JSON-RPC command.
|
||||
type GetBlockHeaderCmd struct {
|
||||
Hash string
|
||||
Verbose *bool `jsonrpcdefault:"true"`
|
||||
}
|
||||
|
||||
// NewGetBlockHeaderCmd returns a new instance which can be used to issue a
|
||||
// getblockheader JSON-RPC command.
|
||||
func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd {
|
||||
return &GetBlockHeaderCmd{
|
||||
Hash: hash,
|
||||
Verbose: verbose,
|
||||
}
|
||||
}
|
||||
|
||||
// TemplateRequest is a request object as defined in BIP22
|
||||
// (https://en.bitcoin.it/wiki/BIP_0022), it is optionally provided as an
|
||||
// pointer argument to GetBlockTemplateCmd.
|
||||
type TemplateRequest struct {
|
||||
Mode string `json:"mode,omitempty"`
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
|
||||
// Optional long polling.
|
||||
LongPollID string `json:"longpollid,omitempty"`
|
||||
|
||||
// Optional template tweaking. SigOpLimit and SizeLimit can be int64
|
||||
// or bool.
|
||||
SigOpLimit interface{} `json:"sigoplimit,omitempty"`
|
||||
SizeLimit interface{} `json:"sizelimit,omitempty"`
|
||||
MaxVersion uint32 `json:"maxversion,omitempty"`
|
||||
|
||||
// Basic pool extension from BIP 0023.
|
||||
Target string `json:"target,omitempty"`
|
||||
|
||||
// Block proposal from BIP 0023. Data is only provided when Mode is
|
||||
// "proposal".
|
||||
Data string `json:"data,omitempty"`
|
||||
WorkID string `json:"workid,omitempty"`
|
||||
}
|
||||
|
||||
// convertTemplateRequestField potentially converts the provided value as
|
||||
// needed.
|
||||
func convertTemplateRequestField(fieldName string, iface interface{}) (interface{}, error) {
|
||||
switch val := iface.(type) {
|
||||
case nil:
|
||||
return nil, nil
|
||||
case bool:
|
||||
return val, nil
|
||||
case float64:
|
||||
if val == float64(int64(val)) {
|
||||
return int64(val), nil
|
||||
}
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("the %s field must be unspecified, a boolean, or "+
|
||||
"a 64-bit integer", fieldName)
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// UnmarshalJSON provides a custom Unmarshal method for TemplateRequest. This
|
||||
// is necessary because the SigOpLimit and SizeLimit fields can only be specific
|
||||
// types.
|
||||
func (t *TemplateRequest) UnmarshalJSON(data []byte) error {
|
||||
type templateRequest TemplateRequest
|
||||
|
||||
request := (*templateRequest)(t)
|
||||
if err := json.Unmarshal(data, &request); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The SigOpLimit field can only be nil, bool, or int64.
|
||||
val, err := convertTemplateRequestField("sigoplimit", request.SigOpLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.SigOpLimit = val
|
||||
|
||||
// The SizeLimit field can only be nil, bool, or int64.
|
||||
val, err = convertTemplateRequestField("sizelimit", request.SizeLimit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
request.SizeLimit = val
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBlockTemplateCmd defines the getblocktemplate JSON-RPC command.
|
||||
type GetBlockTemplateCmd struct {
|
||||
Request *TemplateRequest
|
||||
}
|
||||
|
||||
// NewGetBlockTemplateCmd returns a new instance which can be used to issue a
|
||||
// getblocktemplate JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetBlockTemplateCmd(request *TemplateRequest) *GetBlockTemplateCmd {
|
||||
return &GetBlockTemplateCmd{
|
||||
Request: request,
|
||||
}
|
||||
}
|
||||
|
||||
// GetCFilterCmd defines the getcfilter JSON-RPC command.
|
||||
type GetCFilterCmd struct {
|
||||
Hash string
|
||||
FilterType wire.FilterType
|
||||
}
|
||||
|
||||
// NewGetCFilterCmd returns a new instance which can be used to issue a
|
||||
// getcfilter JSON-RPC command.
|
||||
func NewGetCFilterCmd(hash string, filterType wire.FilterType) *GetCFilterCmd {
|
||||
return &GetCFilterCmd{
|
||||
Hash: hash,
|
||||
FilterType: filterType,
|
||||
}
|
||||
}
|
||||
|
||||
// GetCFilterHeaderCmd defines the getcfilterheader JSON-RPC command.
|
||||
type GetCFilterHeaderCmd struct {
|
||||
Hash string
|
||||
FilterType wire.FilterType
|
||||
}
|
||||
|
||||
// NewGetCFilterHeaderCmd returns a new instance which can be used to issue a
|
||||
// getcfilterheader JSON-RPC command.
|
||||
func NewGetCFilterHeaderCmd(hash string,
|
||||
filterType wire.FilterType) *GetCFilterHeaderCmd {
|
||||
return &GetCFilterHeaderCmd{
|
||||
Hash: hash,
|
||||
FilterType: filterType,
|
||||
}
|
||||
}
|
||||
|
||||
// GetChainTipsCmd defines the getchaintips JSON-RPC command.
|
||||
type GetChainTipsCmd struct{}
|
||||
|
||||
// NewGetChainTipsCmd returns a new instance which can be used to issue a
|
||||
// getchaintips JSON-RPC command.
|
||||
func NewGetChainTipsCmd() *GetChainTipsCmd {
|
||||
return &GetChainTipsCmd{}
|
||||
}
|
||||
|
||||
// GetConnectionCountCmd defines the getconnectioncount JSON-RPC command.
|
||||
type GetConnectionCountCmd struct{}
|
||||
|
||||
// NewGetConnectionCountCmd returns a new instance which can be used to issue a
|
||||
// getconnectioncount JSON-RPC command.
|
||||
func NewGetConnectionCountCmd() *GetConnectionCountCmd {
|
||||
return &GetConnectionCountCmd{}
|
||||
}
|
||||
|
||||
// GetDifficultyCmd defines the getdifficulty JSON-RPC command.
|
||||
type GetDifficultyCmd struct{}
|
||||
|
||||
// NewGetDifficultyCmd returns a new instance which can be used to issue a
|
||||
// getdifficulty JSON-RPC command.
|
||||
func NewGetDifficultyCmd() *GetDifficultyCmd {
|
||||
return &GetDifficultyCmd{}
|
||||
}
|
||||
|
||||
// GetGenerateCmd defines the getgenerate JSON-RPC command.
|
||||
type GetGenerateCmd struct{}
|
||||
|
||||
// NewGetGenerateCmd returns a new instance which can be used to issue a
|
||||
// getgenerate JSON-RPC command.
|
||||
func NewGetGenerateCmd() *GetGenerateCmd {
|
||||
return &GetGenerateCmd{}
|
||||
}
|
||||
|
||||
// GetHashesPerSecCmd defines the gethashespersec JSON-RPC command.
|
||||
type GetHashesPerSecCmd struct{}
|
||||
|
||||
// NewGetHashesPerSecCmd returns a new instance which can be used to issue a
|
||||
// gethashespersec JSON-RPC command.
|
||||
func NewGetHashesPerSecCmd() *GetHashesPerSecCmd {
|
||||
return &GetHashesPerSecCmd{}
|
||||
}
|
||||
|
||||
// GetInfoCmd defines the getinfo JSON-RPC command.
|
||||
type GetInfoCmd struct{}
|
||||
|
||||
// NewGetInfoCmd returns a new instance which can be used to issue a
|
||||
// getinfo JSON-RPC command.
|
||||
func NewGetInfoCmd() *GetInfoCmd {
|
||||
return &GetInfoCmd{}
|
||||
}
|
||||
|
||||
// GetMempoolEntryCmd defines the getmempoolentry JSON-RPC command.
|
||||
type GetMempoolEntryCmd struct {
|
||||
TxID string
|
||||
}
|
||||
|
||||
// NewGetMempoolEntryCmd returns a new instance which can be used to issue a
|
||||
// getmempoolentry JSON-RPC command.
|
||||
func NewGetMempoolEntryCmd(txHash string) *GetMempoolEntryCmd {
|
||||
return &GetMempoolEntryCmd{
|
||||
TxID: txHash,
|
||||
}
|
||||
}
|
||||
|
||||
// GetMempoolInfoCmd defines the getmempoolinfo JSON-RPC command.
|
||||
type GetMempoolInfoCmd struct{}
|
||||
|
||||
// NewGetMempoolInfoCmd returns a new instance which can be used to issue a
|
||||
// getmempool JSON-RPC command.
|
||||
func NewGetMempoolInfoCmd() *GetMempoolInfoCmd {
|
||||
return &GetMempoolInfoCmd{}
|
||||
}
|
||||
|
||||
// GetMiningInfoCmd defines the getmininginfo JSON-RPC command.
|
||||
type GetMiningInfoCmd struct{}
|
||||
|
||||
// NewGetMiningInfoCmd returns a new instance which can be used to issue a
|
||||
// getmininginfo JSON-RPC command.
|
||||
func NewGetMiningInfoCmd() *GetMiningInfoCmd {
|
||||
return &GetMiningInfoCmd{}
|
||||
}
|
||||
|
||||
// GetNetworkInfoCmd defines the getnetworkinfo JSON-RPC command.
|
||||
type GetNetworkInfoCmd struct{}
|
||||
|
||||
// NewGetNetworkInfoCmd returns a new instance which can be used to issue a
|
||||
// getnetworkinfo JSON-RPC command.
|
||||
func NewGetNetworkInfoCmd() *GetNetworkInfoCmd {
|
||||
return &GetNetworkInfoCmd{}
|
||||
}
|
||||
|
||||
// GetNetTotalsCmd defines the getnettotals JSON-RPC command.
|
||||
type GetNetTotalsCmd struct{}
|
||||
|
||||
// NewGetNetTotalsCmd returns a new instance which can be used to issue a
|
||||
// getnettotals JSON-RPC command.
|
||||
func NewGetNetTotalsCmd() *GetNetTotalsCmd {
|
||||
return &GetNetTotalsCmd{}
|
||||
}
|
||||
|
||||
// GetNetworkHashPSCmd defines the getnetworkhashps JSON-RPC command.
|
||||
type GetNetworkHashPSCmd struct {
|
||||
Blocks *int `jsonrpcdefault:"120"`
|
||||
Height *int `jsonrpcdefault:"-1"`
|
||||
}
|
||||
|
||||
// NewGetNetworkHashPSCmd returns a new instance which can be used to issue a
|
||||
// getnetworkhashps JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetNetworkHashPSCmd(numBlocks, height *int) *GetNetworkHashPSCmd {
|
||||
return &GetNetworkHashPSCmd{
|
||||
Blocks: numBlocks,
|
||||
Height: height,
|
||||
}
|
||||
}
|
||||
|
||||
// GetPeerInfoCmd defines the getpeerinfo JSON-RPC command.
|
||||
type GetPeerInfoCmd struct{}
|
||||
|
||||
// NewGetPeerInfoCmd returns a new instance which can be used to issue a getpeer
|
||||
// JSON-RPC command.
|
||||
func NewGetPeerInfoCmd() *GetPeerInfoCmd {
|
||||
return &GetPeerInfoCmd{}
|
||||
}
|
||||
|
||||
// GetRawMempoolCmd defines the getmempool JSON-RPC command.
|
||||
type GetRawMempoolCmd struct {
|
||||
Verbose *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewGetRawMempoolCmd returns a new instance which can be used to issue a
|
||||
// getrawmempool JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetRawMempoolCmd(verbose *bool) *GetRawMempoolCmd {
|
||||
return &GetRawMempoolCmd{
|
||||
Verbose: verbose,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRawTransactionCmd defines the getrawtransaction JSON-RPC command.
|
||||
//
|
||||
// NOTE: This field is an int versus a bool to remain compatible with Bitcoin
|
||||
// Core even though it really should be a bool.
|
||||
type GetRawTransactionCmd struct {
|
||||
Txid string
|
||||
Verbose *int `jsonrpcdefault:"0"`
|
||||
}
|
||||
|
||||
// NewGetRawTransactionCmd returns a new instance which can be used to issue a
|
||||
// getrawtransaction JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetRawTransactionCmd(txHash string, verbose *int) *GetRawTransactionCmd {
|
||||
return &GetRawTransactionCmd{
|
||||
Txid: txHash,
|
||||
Verbose: verbose,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTxOutCmd defines the gettxout JSON-RPC command.
|
||||
type GetTxOutCmd struct {
|
||||
Txid string
|
||||
Vout uint32
|
||||
IncludeMempool *bool `jsonrpcdefault:"true"`
|
||||
}
|
||||
|
||||
// NewGetTxOutCmd returns a new instance which can be used to issue a gettxout
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetTxOutCmd(txHash string, vout uint32, includeMempool *bool) *GetTxOutCmd {
|
||||
return &GetTxOutCmd{
|
||||
Txid: txHash,
|
||||
Vout: vout,
|
||||
IncludeMempool: includeMempool,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTxOutProofCmd defines the gettxoutproof JSON-RPC command.
|
||||
type GetTxOutProofCmd struct {
|
||||
TxIDs []string
|
||||
BlockHash *string
|
||||
}
|
||||
|
||||
// NewGetTxOutProofCmd returns a new instance which can be used to issue a
|
||||
// gettxoutproof JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetTxOutProofCmd(txIDs []string, blockHash *string) *GetTxOutProofCmd {
|
||||
return &GetTxOutProofCmd{
|
||||
TxIDs: txIDs,
|
||||
BlockHash: blockHash,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTxOutSetInfoCmd defines the gettxoutsetinfo JSON-RPC command.
|
||||
type GetTxOutSetInfoCmd struct{}
|
||||
|
||||
// NewGetTxOutSetInfoCmd returns a new instance which can be used to issue a
|
||||
// gettxoutsetinfo JSON-RPC command.
|
||||
func NewGetTxOutSetInfoCmd() *GetTxOutSetInfoCmd {
|
||||
return &GetTxOutSetInfoCmd{}
|
||||
}
|
||||
|
||||
// GetWorkCmd defines the getwork JSON-RPC command.
|
||||
type GetWorkCmd struct {
|
||||
Data *string
|
||||
}
|
||||
|
||||
// NewGetWorkCmd returns a new instance which can be used to issue a getwork
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetWorkCmd(data *string) *GetWorkCmd {
|
||||
return &GetWorkCmd{
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// HelpCmd defines the help JSON-RPC command.
|
||||
type HelpCmd struct {
|
||||
Command *string
|
||||
}
|
||||
|
||||
// NewHelpCmd returns a new instance which can be used to issue a help JSON-RPC
|
||||
// command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewHelpCmd(command *string) *HelpCmd {
|
||||
return &HelpCmd{
|
||||
Command: command,
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateBlockCmd defines the invalidateblock JSON-RPC command.
|
||||
type InvalidateBlockCmd struct {
|
||||
BlockHash string
|
||||
}
|
||||
|
||||
// NewInvalidateBlockCmd returns a new instance which can be used to issue a
|
||||
// invalidateblock JSON-RPC command.
|
||||
func NewInvalidateBlockCmd(blockHash string) *InvalidateBlockCmd {
|
||||
return &InvalidateBlockCmd{
|
||||
BlockHash: blockHash,
|
||||
}
|
||||
}
|
||||
|
||||
// PingCmd defines the ping JSON-RPC command.
|
||||
type PingCmd struct{}
|
||||
|
||||
// NewPingCmd returns a new instance which can be used to issue a ping JSON-RPC
|
||||
// command.
|
||||
func NewPingCmd() *PingCmd {
|
||||
return &PingCmd{}
|
||||
}
|
||||
|
||||
// PreciousBlockCmd defines the preciousblock JSON-RPC command.
|
||||
type PreciousBlockCmd struct {
|
||||
BlockHash string
|
||||
}
|
||||
|
||||
// NewPreciousBlockCmd returns a new instance which can be used to issue a
|
||||
// preciousblock JSON-RPC command.
|
||||
func NewPreciousBlockCmd(blockHash string) *PreciousBlockCmd {
|
||||
return &PreciousBlockCmd{
|
||||
BlockHash: blockHash,
|
||||
}
|
||||
}
|
||||
|
||||
// ReconsiderBlockCmd defines the reconsiderblock JSON-RPC command.
|
||||
type ReconsiderBlockCmd struct {
|
||||
BlockHash string
|
||||
}
|
||||
|
||||
// NewReconsiderBlockCmd returns a new instance which can be used to issue a
|
||||
// reconsiderblock JSON-RPC command.
|
||||
func NewReconsiderBlockCmd(blockHash string) *ReconsiderBlockCmd {
|
||||
return &ReconsiderBlockCmd{
|
||||
BlockHash: blockHash,
|
||||
}
|
||||
}
|
||||
|
||||
// SearchRawTransactionsCmd defines the searchrawtransactions JSON-RPC command.
|
||||
type SearchRawTransactionsCmd struct {
|
||||
Address string
|
||||
Verbose *int `jsonrpcdefault:"1"`
|
||||
Skip *int `jsonrpcdefault:"0"`
|
||||
Count *int `jsonrpcdefault:"100"`
|
||||
VinExtra *int `jsonrpcdefault:"0"`
|
||||
Reverse *bool `jsonrpcdefault:"false"`
|
||||
FilterAddrs *[]string
|
||||
}
|
||||
|
||||
// NewSearchRawTransactionsCmd returns a new instance which can be used to issue a
|
||||
// sendrawtransaction JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSearchRawTransactionsCmd(address string, verbose, skip, count *int, vinExtra *int, reverse *bool, filterAddrs *[]string) *SearchRawTransactionsCmd {
|
||||
return &SearchRawTransactionsCmd{
|
||||
Address: address,
|
||||
Verbose: verbose,
|
||||
Skip: skip,
|
||||
Count: count,
|
||||
VinExtra: vinExtra,
|
||||
Reverse: reverse,
|
||||
FilterAddrs: filterAddrs,
|
||||
}
|
||||
}
|
||||
|
||||
// SendRawTransactionCmd defines the sendrawtransaction JSON-RPC command.
|
||||
type SendRawTransactionCmd struct {
|
||||
HexTx string
|
||||
AllowHighFees *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewSendRawTransactionCmd returns a new instance which can be used to issue a
|
||||
// sendrawtransaction JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSendRawTransactionCmd(hexTx string, allowHighFees *bool) *SendRawTransactionCmd {
|
||||
return &SendRawTransactionCmd{
|
||||
HexTx: hexTx,
|
||||
AllowHighFees: allowHighFees,
|
||||
}
|
||||
}
|
||||
|
||||
// SetGenerateCmd defines the setgenerate JSON-RPC command.
|
||||
type SetGenerateCmd struct {
|
||||
Generate bool
|
||||
GenProcLimit *int `jsonrpcdefault:"-1"`
|
||||
}
|
||||
|
||||
// NewSetGenerateCmd returns a new instance which can be used to issue a
|
||||
// setgenerate JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSetGenerateCmd(generate bool, genProcLimit *int) *SetGenerateCmd {
|
||||
return &SetGenerateCmd{
|
||||
Generate: generate,
|
||||
GenProcLimit: genProcLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// StopCmd defines the stop JSON-RPC command.
|
||||
type StopCmd struct{}
|
||||
|
||||
// NewStopCmd returns a new instance which can be used to issue a stop JSON-RPC
|
||||
// command.
|
||||
func NewStopCmd() *StopCmd {
|
||||
return &StopCmd{}
|
||||
}
|
||||
|
||||
// SubmitBlockOptions represents the optional options struct provided with a
|
||||
// SubmitBlockCmd command.
|
||||
type SubmitBlockOptions struct {
|
||||
// must be provided if server provided a workid with template.
|
||||
WorkID string `json:"workid,omitempty"`
|
||||
}
|
||||
|
||||
// SubmitBlockCmd defines the submitblock JSON-RPC command.
|
||||
type SubmitBlockCmd struct {
|
||||
HexBlock string
|
||||
Options *SubmitBlockOptions
|
||||
}
|
||||
|
||||
// NewSubmitBlockCmd returns a new instance which can be used to issue a
|
||||
// submitblock JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSubmitBlockCmd(hexBlock string, options *SubmitBlockOptions) *SubmitBlockCmd {
|
||||
return &SubmitBlockCmd{
|
||||
HexBlock: hexBlock,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// UptimeCmd defines the uptime JSON-RPC command.
|
||||
type UptimeCmd struct{}
|
||||
|
||||
// NewUptimeCmd returns a new instance which can be used to issue an uptime JSON-RPC command.
|
||||
func NewUptimeCmd() *UptimeCmd {
|
||||
return &UptimeCmd{}
|
||||
}
|
||||
|
||||
// ValidateAddressCmd defines the validateaddress JSON-RPC command.
|
||||
type ValidateAddressCmd struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
// NewValidateAddressCmd returns a new instance which can be used to issue a
|
||||
// validateaddress JSON-RPC command.
|
||||
func NewValidateAddressCmd(address string) *ValidateAddressCmd {
|
||||
return &ValidateAddressCmd{
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyChainCmd defines the verifychain JSON-RPC command.
|
||||
type VerifyChainCmd struct {
|
||||
CheckLevel *int32 `jsonrpcdefault:"3"`
|
||||
CheckDepth *int32 `jsonrpcdefault:"288"` // 0 = all
|
||||
}
|
||||
|
||||
// NewVerifyChainCmd returns a new instance which can be used to issue a
|
||||
// verifychain JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewVerifyChainCmd(checkLevel, checkDepth *int32) *VerifyChainCmd {
|
||||
return &VerifyChainCmd{
|
||||
CheckLevel: checkLevel,
|
||||
CheckDepth: checkDepth,
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyMessageCmd defines the verifymessage JSON-RPC command.
|
||||
type VerifyMessageCmd struct {
|
||||
Address string
|
||||
Signature string
|
||||
Message string
|
||||
}
|
||||
|
||||
// NewVerifyMessageCmd returns a new instance which can be used to issue a
|
||||
// verifymessage JSON-RPC command.
|
||||
func NewVerifyMessageCmd(address, signature, message string) *VerifyMessageCmd {
|
||||
return &VerifyMessageCmd{
|
||||
Address: address,
|
||||
Signature: signature,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// VerifyTxOutProofCmd defines the verifytxoutproof JSON-RPC command.
|
||||
type VerifyTxOutProofCmd struct {
|
||||
Proof string
|
||||
}
|
||||
|
||||
// NewVerifyTxOutProofCmd returns a new instance which can be used to issue a
|
||||
// verifytxoutproof JSON-RPC command.
|
||||
func NewVerifyTxOutProofCmd(proof string) *VerifyTxOutProofCmd {
|
||||
return &VerifyTxOutProofCmd{
|
||||
Proof: proof,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// No special flags for commands in this file.
|
||||
flags := UsageFlag(0)
|
||||
|
||||
MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags)
|
||||
MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags)
|
||||
MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getbestblockhash", (*GetBestBlockHashCmd)(nil), flags)
|
||||
MustRegisterCmd("getblock", (*GetBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("getblockchaininfo", (*GetBlockChainInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getblockcount", (*GetBlockCountCmd)(nil), flags)
|
||||
MustRegisterCmd("getblockhash", (*GetBlockHashCmd)(nil), flags)
|
||||
MustRegisterCmd("getblockheader", (*GetBlockHeaderCmd)(nil), flags)
|
||||
MustRegisterCmd("getblocktemplate", (*GetBlockTemplateCmd)(nil), flags)
|
||||
MustRegisterCmd("getcfilter", (*GetCFilterCmd)(nil), flags)
|
||||
MustRegisterCmd("getcfilterheader", (*GetCFilterHeaderCmd)(nil), flags)
|
||||
MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags)
|
||||
MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags)
|
||||
MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags)
|
||||
MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags)
|
||||
MustRegisterCmd("gethashespersec", (*GetHashesPerSecCmd)(nil), flags)
|
||||
MustRegisterCmd("getinfo", (*GetInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getmempoolentry", (*GetMempoolEntryCmd)(nil), flags)
|
||||
MustRegisterCmd("getmempoolinfo", (*GetMempoolInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getmininginfo", (*GetMiningInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getnetworkinfo", (*GetNetworkInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getnettotals", (*GetNetTotalsCmd)(nil), flags)
|
||||
MustRegisterCmd("getnetworkhashps", (*GetNetworkHashPSCmd)(nil), flags)
|
||||
MustRegisterCmd("getpeerinfo", (*GetPeerInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getrawmempool", (*GetRawMempoolCmd)(nil), flags)
|
||||
MustRegisterCmd("getrawtransaction", (*GetRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("gettxout", (*GetTxOutCmd)(nil), flags)
|
||||
MustRegisterCmd("gettxoutproof", (*GetTxOutProofCmd)(nil), flags)
|
||||
MustRegisterCmd("gettxoutsetinfo", (*GetTxOutSetInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("getwork", (*GetWorkCmd)(nil), flags)
|
||||
MustRegisterCmd("help", (*HelpCmd)(nil), flags)
|
||||
MustRegisterCmd("invalidateblock", (*InvalidateBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("ping", (*PingCmd)(nil), flags)
|
||||
MustRegisterCmd("preciousblock", (*PreciousBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("reconsiderblock", (*ReconsiderBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("searchrawtransactions", (*SearchRawTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("sendrawtransaction", (*SendRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("setgenerate", (*SetGenerateCmd)(nil), flags)
|
||||
MustRegisterCmd("stop", (*StopCmd)(nil), flags)
|
||||
MustRegisterCmd("submitblock", (*SubmitBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("uptime", (*UptimeCmd)(nil), flags)
|
||||
MustRegisterCmd("validateaddress", (*ValidateAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("verifychain", (*VerifyChainCmd)(nil), flags)
|
||||
MustRegisterCmd("verifymessage", (*VerifyMessageCmd)(nil), flags)
|
||||
MustRegisterCmd("verifytxoutproof", (*VerifyTxOutProofCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,550 @@
|
|||
// Copyright (c) 2014-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
|
||||
// the verbose flag is set. When the verbose flag is not set, getblockheader
|
||||
// returns a hex-encoded string.
|
||||
type GetBlockHeaderVerboseResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Height int32 `json:"height"`
|
||||
Version int32 `json:"version"`
|
||||
VersionHex string `json:"versionHex"`
|
||||
MerkleRoot string `json:"merkleroot"`
|
||||
Time int64 `json:"time"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
Bits string `json:"bits"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
PreviousHash string `json:"previousblockhash,omitempty"`
|
||||
NextHash string `json:"nextblockhash,omitempty"`
|
||||
}
|
||||
|
||||
// GetBlockVerboseResult models the data from the getblock command when the
|
||||
// verbose flag is set. When the verbose flag is not set, getblock returns a
|
||||
// hex-encoded string.
|
||||
type GetBlockVerboseResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
StrippedSize int32 `json:"strippedsize"`
|
||||
Size int32 `json:"size"`
|
||||
Weight int32 `json:"weight"`
|
||||
Height int64 `json:"height"`
|
||||
Version int32 `json:"version"`
|
||||
VersionHex string `json:"versionHex"`
|
||||
MerkleRoot string `json:"merkleroot"`
|
||||
Tx []string `json:"tx,omitempty"`
|
||||
RawTx []TxRawResult `json:"rawtx,omitempty"`
|
||||
Time int64 `json:"time"`
|
||||
Nonce uint32 `json:"nonce"`
|
||||
Bits string `json:"bits"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
PreviousHash string `json:"previousblockhash"`
|
||||
NextHash string `json:"nextblockhash,omitempty"`
|
||||
}
|
||||
|
||||
// CreateMultiSigResult models the data returned from the createmultisig
|
||||
// command.
|
||||
type CreateMultiSigResult struct {
|
||||
Address string `json:"address"`
|
||||
RedeemScript string `json:"redeemScript"`
|
||||
}
|
||||
|
||||
// DecodeScriptResult models the data returned from the decodescript command.
|
||||
type DecodeScriptResult struct {
|
||||
Asm string `json:"asm"`
|
||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Addresses []string `json:"addresses,omitempty"`
|
||||
P2sh string `json:"p2sh,omitempty"`
|
||||
}
|
||||
|
||||
// GetAddedNodeInfoResultAddr models the data of the addresses portion of the
|
||||
// getaddednodeinfo command.
|
||||
type GetAddedNodeInfoResultAddr struct {
|
||||
Address string `json:"address"`
|
||||
Connected string `json:"connected"`
|
||||
}
|
||||
|
||||
// GetAddedNodeInfoResult models the data from the getaddednodeinfo command.
|
||||
type GetAddedNodeInfoResult struct {
|
||||
AddedNode string `json:"addednode"`
|
||||
Connected *bool `json:"connected,omitempty"`
|
||||
Addresses *[]GetAddedNodeInfoResultAddr `json:"addresses,omitempty"`
|
||||
}
|
||||
|
||||
// SoftForkDescription describes the current state of a soft-fork which was
|
||||
// deployed using a super-majority block signalling.
|
||||
type SoftForkDescription struct {
|
||||
ID string `json:"id"`
|
||||
Version uint32 `json:"version"`
|
||||
Reject struct {
|
||||
Status bool `json:"status"`
|
||||
} `json:"reject"`
|
||||
}
|
||||
|
||||
// Bip9SoftForkDescription describes the current state of a defined BIP0009
|
||||
// version bits soft-fork.
|
||||
type Bip9SoftForkDescription struct {
|
||||
Status string `json:"status"`
|
||||
Bit uint8 `json:"bit"`
|
||||
StartTime int64 `json:"startTime"`
|
||||
Timeout int64 `json:"timeout"`
|
||||
Since int32 `json:"since"`
|
||||
}
|
||||
|
||||
// GetBlockChainInfoResult models the data returned from the getblockchaininfo
|
||||
// command.
|
||||
type GetBlockChainInfoResult struct {
|
||||
Chain string `json:"chain"`
|
||||
Blocks int32 `json:"blocks"`
|
||||
Headers int32 `json:"headers"`
|
||||
BestBlockHash string `json:"bestblockhash"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
MedianTime int64 `json:"mediantime"`
|
||||
VerificationProgress float64 `json:"verificationprogress,omitempty"`
|
||||
Pruned bool `json:"pruned"`
|
||||
PruneHeight int32 `json:"pruneheight,omitempty"`
|
||||
ChainWork string `json:"chainwork,omitempty"`
|
||||
SoftForks []*SoftForkDescription `json:"softforks"`
|
||||
Bip9SoftForks map[string]*Bip9SoftForkDescription `json:"bip9_softforks"`
|
||||
}
|
||||
|
||||
// GetBlockTemplateResultTx models the transactions field of the
|
||||
// getblocktemplate command.
|
||||
type GetBlockTemplateResultTx struct {
|
||||
Data string `json:"data"`
|
||||
Hash string `json:"hash"`
|
||||
Depends []int64 `json:"depends"`
|
||||
Fee int64 `json:"fee"`
|
||||
SigOps int64 `json:"sigops"`
|
||||
Weight int64 `json:"weight"`
|
||||
}
|
||||
|
||||
// GetBlockTemplateResultAux models the coinbaseaux field of the
|
||||
// getblocktemplate command.
|
||||
type GetBlockTemplateResultAux struct {
|
||||
Flags string `json:"flags"`
|
||||
}
|
||||
|
||||
// GetBlockTemplateResult models the data returned from the getblocktemplate
|
||||
// command.
|
||||
type GetBlockTemplateResult struct {
|
||||
// Base fields from BIP 0022. CoinbaseAux is optional. One of
|
||||
// CoinbaseTxn or CoinbaseValue must be specified, but not both.
|
||||
Bits string `json:"bits"`
|
||||
CurTime int64 `json:"curtime"`
|
||||
Height int64 `json:"height"`
|
||||
PreviousHash string `json:"previousblockhash"`
|
||||
SigOpLimit int64 `json:"sigoplimit,omitempty"`
|
||||
SizeLimit int64 `json:"sizelimit,omitempty"`
|
||||
WeightLimit int64 `json:"weightlimit,omitempty"`
|
||||
Transactions []GetBlockTemplateResultTx `json:"transactions"`
|
||||
Version int32 `json:"version"`
|
||||
CoinbaseAux *GetBlockTemplateResultAux `json:"coinbaseaux,omitempty"`
|
||||
CoinbaseTxn *GetBlockTemplateResultTx `json:"coinbasetxn,omitempty"`
|
||||
CoinbaseValue *int64 `json:"coinbasevalue,omitempty"`
|
||||
WorkID string `json:"workid,omitempty"`
|
||||
|
||||
// Witness commitment defined in BIP 0141.
|
||||
DefaultWitnessCommitment string `json:"default_witness_commitment,omitempty"`
|
||||
|
||||
// Optional long polling from BIP 0022.
|
||||
LongPollID string `json:"longpollid,omitempty"`
|
||||
LongPollURI string `json:"longpolluri,omitempty"`
|
||||
SubmitOld *bool `json:"submitold,omitempty"`
|
||||
|
||||
// Basic pool extension from BIP 0023.
|
||||
Target string `json:"target,omitempty"`
|
||||
Expires int64 `json:"expires,omitempty"`
|
||||
|
||||
// Mutations from BIP 0023.
|
||||
MaxTime int64 `json:"maxtime,omitempty"`
|
||||
MinTime int64 `json:"mintime,omitempty"`
|
||||
Mutable []string `json:"mutable,omitempty"`
|
||||
NonceRange string `json:"noncerange,omitempty"`
|
||||
|
||||
// Block proposal from BIP 0023.
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
RejectReasion string `json:"reject-reason,omitempty"`
|
||||
}
|
||||
|
||||
// GetMempoolEntryResult models the data returned from the getmempoolentry
|
||||
// command.
|
||||
type GetMempoolEntryResult struct {
|
||||
Size int32 `json:"size"`
|
||||
Fee float64 `json:"fee"`
|
||||
ModifiedFee float64 `json:"modifiedfee"`
|
||||
Time int64 `json:"time"`
|
||||
Height int64 `json:"height"`
|
||||
StartingPriority float64 `json:"startingpriority"`
|
||||
CurrentPriority float64 `json:"currentpriority"`
|
||||
DescendantCount int64 `json:"descendantcount"`
|
||||
DescendantSize int64 `json:"descendantsize"`
|
||||
DescendantFees float64 `json:"descendantfees"`
|
||||
AncestorCount int64 `json:"ancestorcount"`
|
||||
AncestorSize int64 `json:"ancestorsize"`
|
||||
AncestorFees float64 `json:"ancestorfees"`
|
||||
Depends []string `json:"depends"`
|
||||
}
|
||||
|
||||
// GetMempoolInfoResult models the data returned from the getmempoolinfo
|
||||
// command.
|
||||
type GetMempoolInfoResult struct {
|
||||
Size int64 `json:"size"`
|
||||
Bytes int64 `json:"bytes"`
|
||||
}
|
||||
|
||||
// NetworksResult models the networks data from the getnetworkinfo command.
|
||||
type NetworksResult struct {
|
||||
Name string `json:"name"`
|
||||
Limited bool `json:"limited"`
|
||||
Reachable bool `json:"reachable"`
|
||||
Proxy string `json:"proxy"`
|
||||
ProxyRandomizeCredentials bool `json:"proxy_randomize_credentials"`
|
||||
}
|
||||
|
||||
// LocalAddressesResult models the localaddresses data from the getnetworkinfo
|
||||
// command.
|
||||
type LocalAddressesResult struct {
|
||||
Address string `json:"address"`
|
||||
Port uint16 `json:"port"`
|
||||
Score int32 `json:"score"`
|
||||
}
|
||||
|
||||
// GetNetworkInfoResult models the data returned from the getnetworkinfo
|
||||
// command.
|
||||
type GetNetworkInfoResult struct {
|
||||
Version int32 `json:"version"`
|
||||
SubVersion string `json:"subversion"`
|
||||
ProtocolVersion int32 `json:"protocolversion"`
|
||||
LocalServices string `json:"localservices"`
|
||||
LocalRelay bool `json:"localrelay"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
Connections int32 `json:"connections"`
|
||||
NetworkActive bool `json:"networkactive"`
|
||||
Networks []NetworksResult `json:"networks"`
|
||||
RelayFee float64 `json:"relayfee"`
|
||||
IncrementalFee float64 `json:"incrementalfee"`
|
||||
LocalAddresses []LocalAddressesResult `json:"localaddresses"`
|
||||
Warnings string `json:"warnings"`
|
||||
}
|
||||
|
||||
// GetPeerInfoResult models the data returned from the getpeerinfo command.
|
||||
type GetPeerInfoResult struct {
|
||||
ID int32 `json:"id"`
|
||||
Addr string `json:"addr"`
|
||||
AddrLocal string `json:"addrlocal,omitempty"`
|
||||
Services string `json:"services"`
|
||||
RelayTxes bool `json:"relaytxes"`
|
||||
LastSend int64 `json:"lastsend"`
|
||||
LastRecv int64 `json:"lastrecv"`
|
||||
BytesSent uint64 `json:"bytessent"`
|
||||
BytesRecv uint64 `json:"bytesrecv"`
|
||||
ConnTime int64 `json:"conntime"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
PingTime float64 `json:"pingtime"`
|
||||
PingWait float64 `json:"pingwait,omitempty"`
|
||||
Version uint32 `json:"version"`
|
||||
SubVer string `json:"subver"`
|
||||
Inbound bool `json:"inbound"`
|
||||
StartingHeight int32 `json:"startingheight"`
|
||||
CurrentHeight int32 `json:"currentheight,omitempty"`
|
||||
BanScore int32 `json:"banscore"`
|
||||
FeeFilter int64 `json:"feefilter"`
|
||||
SyncNode bool `json:"syncnode"`
|
||||
}
|
||||
|
||||
// GetRawMempoolVerboseResult models the data returned from the getrawmempool
|
||||
// command when the verbose flag is set. When the verbose flag is not set,
|
||||
// getrawmempool returns an array of transaction hashes.
|
||||
type GetRawMempoolVerboseResult struct {
|
||||
Size int32 `json:"size"`
|
||||
Vsize int32 `json:"vsize"`
|
||||
Fee float64 `json:"fee"`
|
||||
Time int64 `json:"time"`
|
||||
Height int64 `json:"height"`
|
||||
StartingPriority float64 `json:"startingpriority"`
|
||||
CurrentPriority float64 `json:"currentpriority"`
|
||||
Depends []string `json:"depends"`
|
||||
}
|
||||
|
||||
// ScriptPubKeyResult models the scriptPubKey data of a tx script. It is
|
||||
// defined separately since it is used by multiple commands.
|
||||
type ScriptPubKeyResult struct {
|
||||
Asm string `json:"asm"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
ReqSigs int32 `json:"reqSigs,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Addresses []string `json:"addresses,omitempty"`
|
||||
}
|
||||
|
||||
// GetTxOutResult models the data from the gettxout command.
|
||||
type GetTxOutResult struct {
|
||||
BestBlock string `json:"bestblock"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Value float64 `json:"value"`
|
||||
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
||||
Coinbase bool `json:"coinbase"`
|
||||
}
|
||||
|
||||
// GetNetTotalsResult models the data returned from the getnettotals command.
|
||||
type GetNetTotalsResult struct {
|
||||
TotalBytesRecv uint64 `json:"totalbytesrecv"`
|
||||
TotalBytesSent uint64 `json:"totalbytessent"`
|
||||
TimeMillis int64 `json:"timemillis"`
|
||||
}
|
||||
|
||||
// ScriptSig models a signature script. It is defined separately since it only
|
||||
// applies to non-coinbase. Therefore the field in the Vin structure needs
|
||||
// to be a pointer.
|
||||
type ScriptSig struct {
|
||||
Asm string `json:"asm"`
|
||||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
// Vin models parts of the tx data. It is defined separately since
|
||||
// getrawtransaction, decoderawtransaction, and searchrawtransaction use the
|
||||
// same structure.
|
||||
type Vin struct {
|
||||
Coinbase string `json:"coinbase"`
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
Witness []string `json:"txinwitness"`
|
||||
}
|
||||
|
||||
// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not.
|
||||
func (v *Vin) IsCoinBase() bool {
|
||||
return len(v.Coinbase) > 0
|
||||
}
|
||||
|
||||
// HasWitness returns a bool to show if a Vin has any witness data associated
|
||||
// with it or not.
|
||||
func (v *Vin) HasWitness() bool {
|
||||
return len(v.Witness) > 0
|
||||
}
|
||||
|
||||
// MarshalJSON provides a custom Marshal method for Vin.
|
||||
func (v *Vin) MarshalJSON() ([]byte, error) {
|
||||
if v.IsCoinBase() {
|
||||
coinbaseStruct := struct {
|
||||
Coinbase string `json:"coinbase"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
Witness []string `json:"witness,omitempty"`
|
||||
}{
|
||||
Coinbase: v.Coinbase,
|
||||
Sequence: v.Sequence,
|
||||
Witness: v.Witness,
|
||||
}
|
||||
return json.Marshal(coinbaseStruct)
|
||||
}
|
||||
|
||||
if v.HasWitness() {
|
||||
txStruct := struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
Witness []string `json:"txinwitness"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}{
|
||||
Txid: v.Txid,
|
||||
Vout: v.Vout,
|
||||
ScriptSig: v.ScriptSig,
|
||||
Witness: v.Witness,
|
||||
Sequence: v.Sequence,
|
||||
}
|
||||
return json.Marshal(txStruct)
|
||||
}
|
||||
|
||||
txStruct := struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}{
|
||||
Txid: v.Txid,
|
||||
Vout: v.Vout,
|
||||
ScriptSig: v.ScriptSig,
|
||||
Sequence: v.Sequence,
|
||||
}
|
||||
return json.Marshal(txStruct)
|
||||
}
|
||||
|
||||
// PrevOut represents previous output for an input Vin.
|
||||
type PrevOut struct {
|
||||
Addresses []string `json:"addresses,omitempty"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
// VinPrevOut is like Vin except it includes PrevOut. It is used by searchrawtransaction
|
||||
type VinPrevOut struct {
|
||||
Coinbase string `json:"coinbase"`
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
Witness []string `json:"txinwitness"`
|
||||
PrevOut *PrevOut `json:"prevOut"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}
|
||||
|
||||
// IsCoinBase returns a bool to show if a Vin is a Coinbase one or not.
|
||||
func (v *VinPrevOut) IsCoinBase() bool {
|
||||
return len(v.Coinbase) > 0
|
||||
}
|
||||
|
||||
// HasWitness returns a bool to show if a Vin has any witness data associated
|
||||
// with it or not.
|
||||
func (v *VinPrevOut) HasWitness() bool {
|
||||
return len(v.Witness) > 0
|
||||
}
|
||||
|
||||
// MarshalJSON provides a custom Marshal method for VinPrevOut.
|
||||
func (v *VinPrevOut) MarshalJSON() ([]byte, error) {
|
||||
if v.IsCoinBase() {
|
||||
coinbaseStruct := struct {
|
||||
Coinbase string `json:"coinbase"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}{
|
||||
Coinbase: v.Coinbase,
|
||||
Sequence: v.Sequence,
|
||||
}
|
||||
return json.Marshal(coinbaseStruct)
|
||||
}
|
||||
|
||||
if v.HasWitness() {
|
||||
txStruct := struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
Witness []string `json:"txinwitness"`
|
||||
PrevOut *PrevOut `json:"prevOut,omitempty"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}{
|
||||
Txid: v.Txid,
|
||||
Vout: v.Vout,
|
||||
ScriptSig: v.ScriptSig,
|
||||
Witness: v.Witness,
|
||||
PrevOut: v.PrevOut,
|
||||
Sequence: v.Sequence,
|
||||
}
|
||||
return json.Marshal(txStruct)
|
||||
}
|
||||
|
||||
txStruct := struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig *ScriptSig `json:"scriptSig"`
|
||||
PrevOut *PrevOut `json:"prevOut,omitempty"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
}{
|
||||
Txid: v.Txid,
|
||||
Vout: v.Vout,
|
||||
ScriptSig: v.ScriptSig,
|
||||
PrevOut: v.PrevOut,
|
||||
Sequence: v.Sequence,
|
||||
}
|
||||
return json.Marshal(txStruct)
|
||||
}
|
||||
|
||||
// Vout models parts of the tx data. It is defined separately since both
|
||||
// getrawtransaction and decoderawtransaction use the same structure.
|
||||
type Vout struct {
|
||||
Value float64 `json:"value"`
|
||||
N uint32 `json:"n"`
|
||||
ScriptPubKey ScriptPubKeyResult `json:"scriptPubKey"`
|
||||
}
|
||||
|
||||
// GetMiningInfoResult models the data from the getmininginfo command.
|
||||
type GetMiningInfoResult struct {
|
||||
Blocks int64 `json:"blocks"`
|
||||
CurrentBlockSize uint64 `json:"currentblocksize"`
|
||||
CurrentBlockWeight uint64 `json:"currentblockweight"`
|
||||
CurrentBlockTx uint64 `json:"currentblocktx"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
Errors string `json:"errors"`
|
||||
Generate bool `json:"generate"`
|
||||
GenProcLimit int32 `json:"genproclimit"`
|
||||
HashesPerSec int64 `json:"hashespersec"`
|
||||
NetworkHashPS int64 `json:"networkhashps"`
|
||||
PooledTx uint64 `json:"pooledtx"`
|
||||
TestNet bool `json:"testnet"`
|
||||
}
|
||||
|
||||
// GetWorkResult models the data from the getwork command.
|
||||
type GetWorkResult struct {
|
||||
Data string `json:"data"`
|
||||
Hash1 string `json:"hash1"`
|
||||
Midstate string `json:"midstate"`
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
// InfoChainResult models the data returned by the chain server getinfo command.
|
||||
type InfoChainResult struct {
|
||||
Version int32 `json:"version"`
|
||||
ProtocolVersion int32 `json:"protocolversion"`
|
||||
Blocks int32 `json:"blocks"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
Connections int32 `json:"connections"`
|
||||
Proxy string `json:"proxy"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
TestNet bool `json:"testnet"`
|
||||
RelayFee float64 `json:"relayfee"`
|
||||
Errors string `json:"errors"`
|
||||
}
|
||||
|
||||
// TxRawResult models the data from the getrawtransaction command.
|
||||
type TxRawResult struct {
|
||||
Hex string `json:"hex"`
|
||||
Txid string `json:"txid"`
|
||||
Hash string `json:"hash,omitempty"`
|
||||
Size int32 `json:"size,omitempty"`
|
||||
Vsize int32 `json:"vsize,omitempty"`
|
||||
Version int32 `json:"version"`
|
||||
LockTime uint32 `json:"locktime"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
BlockHash string `json:"blockhash,omitempty"`
|
||||
Confirmations uint64 `json:"confirmations,omitempty"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime,omitempty"`
|
||||
}
|
||||
|
||||
// SearchRawTransactionsResult models the data from the searchrawtransaction
|
||||
// command.
|
||||
type SearchRawTransactionsResult struct {
|
||||
Hex string `json:"hex,omitempty"`
|
||||
Txid string `json:"txid"`
|
||||
Hash string `json:"hash"`
|
||||
Size string `json:"size"`
|
||||
Vsize string `json:"vsize"`
|
||||
Version int32 `json:"version"`
|
||||
LockTime uint32 `json:"locktime"`
|
||||
Vin []VinPrevOut `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
BlockHash string `json:"blockhash,omitempty"`
|
||||
Confirmations uint64 `json:"confirmations,omitempty"`
|
||||
Time int64 `json:"time,omitempty"`
|
||||
Blocktime int64 `json:"blocktime,omitempty"`
|
||||
}
|
||||
|
||||
// TxRawDecodeResult models the data from the decoderawtransaction command.
|
||||
type TxRawDecodeResult struct {
|
||||
Txid string `json:"txid"`
|
||||
Version int32 `json:"version"`
|
||||
Locktime uint32 `json:"locktime"`
|
||||
Vin []Vin `json:"vin"`
|
||||
Vout []Vout `json:"vout"`
|
||||
}
|
||||
|
||||
// ValidateAddressChainResult models the data returned by the chain server
|
||||
// validateaddress command.
|
||||
type ValidateAddressChainResult struct {
|
||||
IsValid bool `json:"isvalid"`
|
||||
Address string `json:"address,omitempty"`
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
// Copyright (c) 2014-2017 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.
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a chain server, but are only available via websockets.
|
||||
|
||||
package btcjson
|
||||
|
||||
// AuthenticateCmd defines the authenticate JSON-RPC command.
|
||||
type AuthenticateCmd struct {
|
||||
Username string
|
||||
Passphrase string
|
||||
}
|
||||
|
||||
// NewAuthenticateCmd returns a new instance which can be used to issue an
|
||||
// authenticate JSON-RPC command.
|
||||
func NewAuthenticateCmd(username, passphrase string) *AuthenticateCmd {
|
||||
return &AuthenticateCmd{
|
||||
Username: username,
|
||||
Passphrase: passphrase,
|
||||
}
|
||||
}
|
||||
|
||||
// NotifyBlocksCmd defines the notifyblocks JSON-RPC command.
|
||||
type NotifyBlocksCmd struct{}
|
||||
|
||||
// NewNotifyBlocksCmd returns a new instance which can be used to issue a
|
||||
// notifyblocks JSON-RPC command.
|
||||
func NewNotifyBlocksCmd() *NotifyBlocksCmd {
|
||||
return &NotifyBlocksCmd{}
|
||||
}
|
||||
|
||||
// StopNotifyBlocksCmd defines the stopnotifyblocks JSON-RPC command.
|
||||
type StopNotifyBlocksCmd struct{}
|
||||
|
||||
// NewStopNotifyBlocksCmd returns a new instance which can be used to issue a
|
||||
// stopnotifyblocks JSON-RPC command.
|
||||
func NewStopNotifyBlocksCmd() *StopNotifyBlocksCmd {
|
||||
return &StopNotifyBlocksCmd{}
|
||||
}
|
||||
|
||||
// NotifyNewTransactionsCmd defines the notifynewtransactions JSON-RPC command.
|
||||
type NotifyNewTransactionsCmd struct {
|
||||
Verbose *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewNotifyNewTransactionsCmd returns a new instance which can be used to issue
|
||||
// a notifynewtransactions JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewNotifyNewTransactionsCmd(verbose *bool) *NotifyNewTransactionsCmd {
|
||||
return &NotifyNewTransactionsCmd{
|
||||
Verbose: verbose,
|
||||
}
|
||||
}
|
||||
|
||||
// SessionCmd defines the session JSON-RPC command.
|
||||
type SessionCmd struct{}
|
||||
|
||||
// NewSessionCmd returns a new instance which can be used to issue a session
|
||||
// JSON-RPC command.
|
||||
func NewSessionCmd() *SessionCmd {
|
||||
return &SessionCmd{}
|
||||
}
|
||||
|
||||
// StopNotifyNewTransactionsCmd defines the stopnotifynewtransactions JSON-RPC command.
|
||||
type StopNotifyNewTransactionsCmd struct{}
|
||||
|
||||
// NewStopNotifyNewTransactionsCmd returns a new instance which can be used to issue
|
||||
// a stopnotifynewtransactions JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewStopNotifyNewTransactionsCmd() *StopNotifyNewTransactionsCmd {
|
||||
return &StopNotifyNewTransactionsCmd{}
|
||||
}
|
||||
|
||||
// NotifyReceivedCmd defines the notifyreceived JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
|
||||
type NotifyReceivedCmd struct {
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// NewNotifyReceivedCmd returns a new instance which can be used to issue a
|
||||
// notifyreceived JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
|
||||
func NewNotifyReceivedCmd(addresses []string) *NotifyReceivedCmd {
|
||||
return &NotifyReceivedCmd{
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// OutPoint describes a transaction outpoint that will be marshalled to and
|
||||
// from JSON.
|
||||
type OutPoint struct {
|
||||
Hash string `json:"hash"`
|
||||
Index uint32 `json:"index"`
|
||||
}
|
||||
|
||||
// LoadTxFilterCmd defines the loadtxfilter request parameters to load or
|
||||
// reload a transaction filter.
|
||||
//
|
||||
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
||||
// and requires a websocket connection.
|
||||
type LoadTxFilterCmd struct {
|
||||
Reload bool
|
||||
Addresses []string
|
||||
OutPoints []OutPoint
|
||||
}
|
||||
|
||||
// NewLoadTxFilterCmd returns a new instance which can be used to issue a
|
||||
// loadtxfilter JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
||||
// and requires a websocket connection.
|
||||
func NewLoadTxFilterCmd(reload bool, addresses []string, outPoints []OutPoint) *LoadTxFilterCmd {
|
||||
return &LoadTxFilterCmd{
|
||||
Reload: reload,
|
||||
Addresses: addresses,
|
||||
OutPoints: outPoints,
|
||||
}
|
||||
}
|
||||
|
||||
// NotifySpentCmd defines the notifyspent JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
|
||||
type NotifySpentCmd struct {
|
||||
OutPoints []OutPoint
|
||||
}
|
||||
|
||||
// NewNotifySpentCmd returns a new instance which can be used to issue a
|
||||
// notifyspent JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
|
||||
func NewNotifySpentCmd(outPoints []OutPoint) *NotifySpentCmd {
|
||||
return &NotifySpentCmd{
|
||||
OutPoints: outPoints,
|
||||
}
|
||||
}
|
||||
|
||||
// StopNotifyReceivedCmd defines the stopnotifyreceived JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
|
||||
type StopNotifyReceivedCmd struct {
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
// NewStopNotifyReceivedCmd returns a new instance which can be used to issue a
|
||||
// stopnotifyreceived JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
|
||||
func NewStopNotifyReceivedCmd(addresses []string) *StopNotifyReceivedCmd {
|
||||
return &StopNotifyReceivedCmd{
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// StopNotifySpentCmd defines the stopnotifyspent JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
|
||||
type StopNotifySpentCmd struct {
|
||||
OutPoints []OutPoint
|
||||
}
|
||||
|
||||
// NewStopNotifySpentCmd returns a new instance which can be used to issue a
|
||||
// stopnotifyspent JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
|
||||
func NewStopNotifySpentCmd(outPoints []OutPoint) *StopNotifySpentCmd {
|
||||
return &StopNotifySpentCmd{
|
||||
OutPoints: outPoints,
|
||||
}
|
||||
}
|
||||
|
||||
// RescanCmd defines the rescan JSON-RPC command.
|
||||
//
|
||||
// NOTE: Deprecated. Use RescanBlocksCmd instead.
|
||||
type RescanCmd struct {
|
||||
BeginBlock string
|
||||
Addresses []string
|
||||
OutPoints []OutPoint
|
||||
EndBlock *string
|
||||
}
|
||||
|
||||
// NewRescanCmd returns a new instance which can be used to issue a rescan
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewRescanBlocksCmd instead.
|
||||
func NewRescanCmd(beginBlock string, addresses []string, outPoints []OutPoint, endBlock *string) *RescanCmd {
|
||||
return &RescanCmd{
|
||||
BeginBlock: beginBlock,
|
||||
Addresses: addresses,
|
||||
OutPoints: outPoints,
|
||||
EndBlock: endBlock,
|
||||
}
|
||||
}
|
||||
|
||||
// RescanBlocksCmd defines the rescan JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
||||
// and requires a websocket connection.
|
||||
type RescanBlocksCmd struct {
|
||||
// Block hashes as a string array.
|
||||
BlockHashes []string
|
||||
}
|
||||
|
||||
// NewRescanBlocksCmd returns a new instance which can be used to issue a rescan
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// NOTE: This is a btcd extension ported from github.com/decred/dcrd/dcrjson
|
||||
// and requires a websocket connection.
|
||||
func NewRescanBlocksCmd(blockHashes []string) *RescanBlocksCmd {
|
||||
return &RescanBlocksCmd{BlockHashes: blockHashes}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable by websockets.
|
||||
flags := UFWebsocketOnly
|
||||
|
||||
MustRegisterCmd("authenticate", (*AuthenticateCmd)(nil), flags)
|
||||
MustRegisterCmd("loadtxfilter", (*LoadTxFilterCmd)(nil), flags)
|
||||
MustRegisterCmd("notifyblocks", (*NotifyBlocksCmd)(nil), flags)
|
||||
MustRegisterCmd("notifynewtransactions", (*NotifyNewTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("notifyreceived", (*NotifyReceivedCmd)(nil), flags)
|
||||
MustRegisterCmd("notifyspent", (*NotifySpentCmd)(nil), flags)
|
||||
MustRegisterCmd("session", (*SessionCmd)(nil), flags)
|
||||
MustRegisterCmd("stopnotifyblocks", (*StopNotifyBlocksCmd)(nil), flags)
|
||||
MustRegisterCmd("stopnotifynewtransactions", (*StopNotifyNewTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("stopnotifyspent", (*StopNotifySpentCmd)(nil), flags)
|
||||
MustRegisterCmd("stopnotifyreceived", (*StopNotifyReceivedCmd)(nil), flags)
|
||||
MustRegisterCmd("rescan", (*RescanCmd)(nil), flags)
|
||||
MustRegisterCmd("rescanblocks", (*RescanBlocksCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright (c) 2014-2017 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.
|
||||
|
||||
// NOTE: This file is intended to house the RPC websocket notifications that are
|
||||
// supported by a chain server.
|
||||
|
||||
package btcjson
|
||||
|
||||
const (
|
||||
// BlockConnectedNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a block has been connected.
|
||||
//
|
||||
// NOTE: Deprecated. Use FilteredBlockConnectedNtfnMethod instead.
|
||||
BlockConnectedNtfnMethod = "blockconnected"
|
||||
|
||||
// BlockDisconnectedNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a block has been
|
||||
// disconnected.
|
||||
//
|
||||
// NOTE: Deprecated. Use FilteredBlockDisconnectedNtfnMethod instead.
|
||||
BlockDisconnectedNtfnMethod = "blockdisconnected"
|
||||
|
||||
// FilteredBlockConnectedNtfnMethod is the new method used for
|
||||
// notifications from the chain server that a block has been connected.
|
||||
FilteredBlockConnectedNtfnMethod = "filteredblockconnected"
|
||||
|
||||
// FilteredBlockDisconnectedNtfnMethod is the new method used for
|
||||
// notifications from the chain server that a block has been
|
||||
// disconnected.
|
||||
FilteredBlockDisconnectedNtfnMethod = "filteredblockdisconnected"
|
||||
|
||||
// RecvTxNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a transaction which pays to
|
||||
// a registered address has been processed.
|
||||
//
|
||||
// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
|
||||
// FilteredBlockConnectedNtfnMethod instead.
|
||||
RecvTxNtfnMethod = "recvtx"
|
||||
|
||||
// RedeemingTxNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a transaction which spends a
|
||||
// registered outpoint has been processed.
|
||||
//
|
||||
// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
|
||||
// FilteredBlockConnectedNtfnMethod instead.
|
||||
RedeemingTxNtfnMethod = "redeemingtx"
|
||||
|
||||
// RescanFinishedNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a legacy, deprecated rescan
|
||||
// operation has finished.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
RescanFinishedNtfnMethod = "rescanfinished"
|
||||
|
||||
// RescanProgressNtfnMethod is the legacy, deprecated method used for
|
||||
// notifications from the chain server that a legacy, deprecated rescan
|
||||
// operation this is underway has made progress.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
RescanProgressNtfnMethod = "rescanprogress"
|
||||
|
||||
// TxAcceptedNtfnMethod is the method used for notifications from the
|
||||
// chain server that a transaction has been accepted into the mempool.
|
||||
TxAcceptedNtfnMethod = "txaccepted"
|
||||
|
||||
// TxAcceptedVerboseNtfnMethod is the method used for notifications from
|
||||
// the chain server that a transaction has been accepted into the
|
||||
// mempool. This differs from TxAcceptedNtfnMethod in that it provides
|
||||
// more details in the notification.
|
||||
TxAcceptedVerboseNtfnMethod = "txacceptedverbose"
|
||||
|
||||
// RelevantTxAcceptedNtfnMethod is the new method used for notifications
|
||||
// from the chain server that inform a client that a transaction that
|
||||
// matches the loaded filter was accepted by the mempool.
|
||||
RelevantTxAcceptedNtfnMethod = "relevanttxaccepted"
|
||||
)
|
||||
|
||||
// BlockConnectedNtfn defines the blockconnected JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use FilteredBlockConnectedNtfn instead.
|
||||
type BlockConnectedNtfn struct {
|
||||
Hash string
|
||||
Height int32
|
||||
Time int64
|
||||
}
|
||||
|
||||
// NewBlockConnectedNtfn returns a new instance which can be used to issue a
|
||||
// blockconnected JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewFilteredBlockConnectedNtfn instead.
|
||||
func NewBlockConnectedNtfn(hash string, height int32, time int64) *BlockConnectedNtfn {
|
||||
return &BlockConnectedNtfn{
|
||||
Hash: hash,
|
||||
Height: height,
|
||||
Time: time,
|
||||
}
|
||||
}
|
||||
|
||||
// BlockDisconnectedNtfn defines the blockdisconnected JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use FilteredBlockDisconnectedNtfn instead.
|
||||
type BlockDisconnectedNtfn struct {
|
||||
Hash string
|
||||
Height int32
|
||||
Time int64
|
||||
}
|
||||
|
||||
// NewBlockDisconnectedNtfn returns a new instance which can be used to issue a
|
||||
// blockdisconnected JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewFilteredBlockDisconnectedNtfn instead.
|
||||
func NewBlockDisconnectedNtfn(hash string, height int32, time int64) *BlockDisconnectedNtfn {
|
||||
return &BlockDisconnectedNtfn{
|
||||
Hash: hash,
|
||||
Height: height,
|
||||
Time: time,
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredBlockConnectedNtfn defines the filteredblockconnected JSON-RPC
|
||||
// notification.
|
||||
type FilteredBlockConnectedNtfn struct {
|
||||
Height int32
|
||||
Header string
|
||||
SubscribedTxs []string
|
||||
}
|
||||
|
||||
// NewFilteredBlockConnectedNtfn returns a new instance which can be used to
|
||||
// issue a filteredblockconnected JSON-RPC notification.
|
||||
func NewFilteredBlockConnectedNtfn(height int32, header string, subscribedTxs []string) *FilteredBlockConnectedNtfn {
|
||||
return &FilteredBlockConnectedNtfn{
|
||||
Height: height,
|
||||
Header: header,
|
||||
SubscribedTxs: subscribedTxs,
|
||||
}
|
||||
}
|
||||
|
||||
// FilteredBlockDisconnectedNtfn defines the filteredblockdisconnected JSON-RPC
|
||||
// notification.
|
||||
type FilteredBlockDisconnectedNtfn struct {
|
||||
Height int32
|
||||
Header string
|
||||
}
|
||||
|
||||
// NewFilteredBlockDisconnectedNtfn returns a new instance which can be used to
|
||||
// issue a filteredblockdisconnected JSON-RPC notification.
|
||||
func NewFilteredBlockDisconnectedNtfn(height int32, header string) *FilteredBlockDisconnectedNtfn {
|
||||
return &FilteredBlockDisconnectedNtfn{
|
||||
Height: height,
|
||||
Header: header,
|
||||
}
|
||||
}
|
||||
|
||||
// BlockDetails describes details of a tx in a block.
|
||||
type BlockDetails struct {
|
||||
Height int32 `json:"height"`
|
||||
Hash string `json:"hash"`
|
||||
Index int `json:"index"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
// RecvTxNtfn defines the recvtx JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
|
||||
// instead.
|
||||
type RecvTxNtfn struct {
|
||||
HexTx string
|
||||
Block *BlockDetails
|
||||
}
|
||||
|
||||
// NewRecvTxNtfn returns a new instance which can be used to issue a recvtx
|
||||
// JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewRelevantTxAcceptedNtfn and
|
||||
// NewFilteredBlockConnectedNtfn instead.
|
||||
func NewRecvTxNtfn(hexTx string, block *BlockDetails) *RecvTxNtfn {
|
||||
return &RecvTxNtfn{
|
||||
HexTx: hexTx,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
// RedeemingTxNtfn defines the redeemingtx JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
|
||||
// instead.
|
||||
type RedeemingTxNtfn struct {
|
||||
HexTx string
|
||||
Block *BlockDetails
|
||||
}
|
||||
|
||||
// NewRedeemingTxNtfn returns a new instance which can be used to issue a
|
||||
// redeemingtx JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Use NewRelevantTxAcceptedNtfn and
|
||||
// NewFilteredBlockConnectedNtfn instead.
|
||||
func NewRedeemingTxNtfn(hexTx string, block *BlockDetails) *RedeemingTxNtfn {
|
||||
return &RedeemingTxNtfn{
|
||||
HexTx: hexTx,
|
||||
Block: block,
|
||||
}
|
||||
}
|
||||
|
||||
// RescanFinishedNtfn defines the rescanfinished JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
type RescanFinishedNtfn struct {
|
||||
Hash string
|
||||
Height int32
|
||||
Time int64
|
||||
}
|
||||
|
||||
// NewRescanFinishedNtfn returns a new instance which can be used to issue a
|
||||
// rescanfinished JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
func NewRescanFinishedNtfn(hash string, height int32, time int64) *RescanFinishedNtfn {
|
||||
return &RescanFinishedNtfn{
|
||||
Hash: hash,
|
||||
Height: height,
|
||||
Time: time,
|
||||
}
|
||||
}
|
||||
|
||||
// RescanProgressNtfn defines the rescanprogress JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
type RescanProgressNtfn struct {
|
||||
Hash string
|
||||
Height int32
|
||||
Time int64
|
||||
}
|
||||
|
||||
// NewRescanProgressNtfn returns a new instance which can be used to issue a
|
||||
// rescanprogress JSON-RPC notification.
|
||||
//
|
||||
// NOTE: Deprecated. Not used with rescanblocks command.
|
||||
func NewRescanProgressNtfn(hash string, height int32, time int64) *RescanProgressNtfn {
|
||||
return &RescanProgressNtfn{
|
||||
Hash: hash,
|
||||
Height: height,
|
||||
Time: time,
|
||||
}
|
||||
}
|
||||
|
||||
// TxAcceptedNtfn defines the txaccepted JSON-RPC notification.
|
||||
type TxAcceptedNtfn struct {
|
||||
TxID string
|
||||
Amount float64
|
||||
}
|
||||
|
||||
// NewTxAcceptedNtfn returns a new instance which can be used to issue a
|
||||
// txaccepted JSON-RPC notification.
|
||||
func NewTxAcceptedNtfn(txHash string, amount float64) *TxAcceptedNtfn {
|
||||
return &TxAcceptedNtfn{
|
||||
TxID: txHash,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
// TxAcceptedVerboseNtfn defines the txacceptedverbose JSON-RPC notification.
|
||||
type TxAcceptedVerboseNtfn struct {
|
||||
RawTx TxRawResult
|
||||
}
|
||||
|
||||
// NewTxAcceptedVerboseNtfn returns a new instance which can be used to issue a
|
||||
// txacceptedverbose JSON-RPC notification.
|
||||
func NewTxAcceptedVerboseNtfn(rawTx TxRawResult) *TxAcceptedVerboseNtfn {
|
||||
return &TxAcceptedVerboseNtfn{
|
||||
RawTx: rawTx,
|
||||
}
|
||||
}
|
||||
|
||||
// RelevantTxAcceptedNtfn defines the parameters to the relevanttxaccepted
|
||||
// JSON-RPC notification.
|
||||
type RelevantTxAcceptedNtfn struct {
|
||||
Transaction string `json:"transaction"`
|
||||
}
|
||||
|
||||
// NewRelevantTxAcceptedNtfn returns a new instance which can be used to issue a
|
||||
// relevantxaccepted JSON-RPC notification.
|
||||
func NewRelevantTxAcceptedNtfn(txHex string) *RelevantTxAcceptedNtfn {
|
||||
return &RelevantTxAcceptedNtfn{Transaction: txHex}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable by websockets and are
|
||||
// notifications.
|
||||
flags := UFWebsocketOnly | UFNotification
|
||||
|
||||
MustRegisterCmd(BlockConnectedNtfnMethod, (*BlockConnectedNtfn)(nil), flags)
|
||||
MustRegisterCmd(BlockDisconnectedNtfnMethod, (*BlockDisconnectedNtfn)(nil), flags)
|
||||
MustRegisterCmd(FilteredBlockConnectedNtfnMethod, (*FilteredBlockConnectedNtfn)(nil), flags)
|
||||
MustRegisterCmd(FilteredBlockDisconnectedNtfnMethod, (*FilteredBlockDisconnectedNtfn)(nil), flags)
|
||||
MustRegisterCmd(RecvTxNtfnMethod, (*RecvTxNtfn)(nil), flags)
|
||||
MustRegisterCmd(RedeemingTxNtfnMethod, (*RedeemingTxNtfn)(nil), flags)
|
||||
MustRegisterCmd(RescanFinishedNtfnMethod, (*RescanFinishedNtfn)(nil), flags)
|
||||
MustRegisterCmd(RescanProgressNtfnMethod, (*RescanProgressNtfn)(nil), flags)
|
||||
MustRegisterCmd(TxAcceptedNtfnMethod, (*TxAcceptedNtfn)(nil), flags)
|
||||
MustRegisterCmd(TxAcceptedVerboseNtfnMethod, (*TxAcceptedVerboseNtfn)(nil), flags)
|
||||
MustRegisterCmd(RelevantTxAcceptedNtfnMethod, (*RelevantTxAcceptedNtfn)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) 2015-2017 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 btcjson
|
||||
|
||||
// SessionResult models the data from the session command.
|
||||
type SessionResult struct {
|
||||
SessionID uint64 `json:"sessionid"`
|
||||
}
|
||||
|
||||
// RescannedBlock contains the hash and all discovered transactions of a single
|
||||
// rescanned block.
|
||||
//
|
||||
// NOTE: This is a btcsuite extension ported from
|
||||
// github.com/decred/dcrd/dcrjson.
|
||||
type RescannedBlock struct {
|
||||
Hash string `json:"hash"`
|
||||
Transactions []string `json:"transactions"`
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CmdMethod returns the method for the passed command. The provided command
|
||||
// type must be a registered type. All commands provided by this package are
|
||||
// registered by default.
|
||||
func CmdMethod(cmd interface{}) (string, error) {
|
||||
// Look up the cmd type and error out if not registered.
|
||||
rt := reflect.TypeOf(cmd)
|
||||
registerLock.RLock()
|
||||
method, ok := concreteTypeToMethod[rt]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return "", makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
return method, nil
|
||||
}
|
||||
|
||||
// MethodUsageFlags returns the usage flags for the passed command method. The
|
||||
// provided method must be associated with a registered type. All commands
|
||||
// provided by this package are registered by default.
|
||||
func MethodUsageFlags(method string) (UsageFlag, error) {
|
||||
// Look up details about the provided method and error out if not
|
||||
// registered.
|
||||
registerLock.RLock()
|
||||
info, ok := methodToInfo[method]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return 0, makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
return info.flags, nil
|
||||
}
|
||||
|
||||
// subStructUsage returns a string for use in the one-line usage for the given
|
||||
// sub struct. Note that this is specifically for fields which consist of
|
||||
// structs (or an array/slice of structs) as opposed to the top-level command
|
||||
// struct.
|
||||
//
|
||||
// Any fields that include a jsonrpcusage struct tag will use that instead of
|
||||
// being automatically generated.
|
||||
func subStructUsage(structType reflect.Type) string {
|
||||
numFields := structType.NumField()
|
||||
fieldUsages := make([]string, 0, numFields)
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
rtf := structType.Field(i)
|
||||
|
||||
// When the field has a jsonrpcusage struct tag specified use
|
||||
// that instead of automatically generating it.
|
||||
if tag := rtf.Tag.Get("jsonrpcusage"); tag != "" {
|
||||
fieldUsages = append(fieldUsages, tag)
|
||||
continue
|
||||
}
|
||||
|
||||
// Create the name/value entry for the field while considering
|
||||
// the type of the field. Not all possible types are covered
|
||||
// here and when one of the types not specifically covered is
|
||||
// encountered, the field name is simply reused for the value.
|
||||
fieldName := strings.ToLower(rtf.Name)
|
||||
fieldValue := fieldName
|
||||
fieldKind := rtf.Type.Kind()
|
||||
switch {
|
||||
case isNumeric(fieldKind):
|
||||
if fieldKind == reflect.Float32 || fieldKind == reflect.Float64 {
|
||||
fieldValue = "n.nnn"
|
||||
} else {
|
||||
fieldValue = "n"
|
||||
}
|
||||
case fieldKind == reflect.String:
|
||||
fieldValue = `"value"`
|
||||
|
||||
case fieldKind == reflect.Struct:
|
||||
fieldValue = subStructUsage(rtf.Type)
|
||||
|
||||
case fieldKind == reflect.Array || fieldKind == reflect.Slice:
|
||||
fieldValue = subArrayUsage(rtf.Type, fieldName)
|
||||
}
|
||||
|
||||
usage := fmt.Sprintf("%q:%s", fieldName, fieldValue)
|
||||
fieldUsages = append(fieldUsages, usage)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("{%s}", strings.Join(fieldUsages, ","))
|
||||
}
|
||||
|
||||
// subArrayUsage returns a string for use in the one-line usage for the given
|
||||
// array or slice. It also contains logic to convert plural field names to
|
||||
// singular so the generated usage string reads better.
|
||||
func subArrayUsage(arrayType reflect.Type, fieldName string) string {
|
||||
// Convert plural field names to singular. Only works for English.
|
||||
singularFieldName := fieldName
|
||||
if strings.HasSuffix(fieldName, "ies") {
|
||||
singularFieldName = strings.TrimSuffix(fieldName, "ies")
|
||||
singularFieldName = singularFieldName + "y"
|
||||
} else if strings.HasSuffix(fieldName, "es") {
|
||||
singularFieldName = strings.TrimSuffix(fieldName, "es")
|
||||
} else if strings.HasSuffix(fieldName, "s") {
|
||||
singularFieldName = strings.TrimSuffix(fieldName, "s")
|
||||
}
|
||||
|
||||
elemType := arrayType.Elem()
|
||||
switch elemType.Kind() {
|
||||
case reflect.String:
|
||||
return fmt.Sprintf("[%q,...]", singularFieldName)
|
||||
|
||||
case reflect.Struct:
|
||||
return fmt.Sprintf("[%s,...]", subStructUsage(elemType))
|
||||
}
|
||||
|
||||
// Fall back to simply showing the field name in array syntax.
|
||||
return fmt.Sprintf(`[%s,...]`, singularFieldName)
|
||||
}
|
||||
|
||||
// fieldUsage returns a string for use in the one-line usage for the struct
|
||||
// field of a command.
|
||||
//
|
||||
// Any fields that include a jsonrpcusage struct tag will use that instead of
|
||||
// being automatically generated.
|
||||
func fieldUsage(structField reflect.StructField, defaultVal *reflect.Value) string {
|
||||
// When the field has a jsonrpcusage struct tag specified use that
|
||||
// instead of automatically generating it.
|
||||
if tag := structField.Tag.Get("jsonrpcusage"); tag != "" {
|
||||
return tag
|
||||
}
|
||||
|
||||
// Indirect the pointer if needed.
|
||||
fieldType := structField.Type
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
|
||||
// When there is a default value, it must also be a pointer due to the
|
||||
// rules enforced by RegisterCmd.
|
||||
if defaultVal != nil {
|
||||
indirect := defaultVal.Elem()
|
||||
defaultVal = &indirect
|
||||
}
|
||||
|
||||
// Handle certain types uniquely to provide nicer usage.
|
||||
fieldName := strings.ToLower(structField.Name)
|
||||
switch fieldType.Kind() {
|
||||
case reflect.String:
|
||||
if defaultVal != nil {
|
||||
return fmt.Sprintf("%s=%q", fieldName,
|
||||
defaultVal.Interface())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%q", fieldName)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
return subArrayUsage(fieldType, fieldName)
|
||||
|
||||
case reflect.Struct:
|
||||
return subStructUsage(fieldType)
|
||||
}
|
||||
|
||||
// Simply return the field name when none of the above special cases
|
||||
// apply.
|
||||
if defaultVal != nil {
|
||||
return fmt.Sprintf("%s=%v", fieldName, defaultVal.Interface())
|
||||
}
|
||||
return fieldName
|
||||
}
|
||||
|
||||
// methodUsageText returns a one-line usage string for the provided command and
|
||||
// method info. This is the main work horse for the exported MethodUsageText
|
||||
// function.
|
||||
func methodUsageText(rtp reflect.Type, defaults map[int]reflect.Value, method string) string {
|
||||
// Generate the individual usage for each field in the command. Several
|
||||
// simplifying assumptions are made here because the RegisterCmd
|
||||
// function has already rigorously enforced the layout.
|
||||
rt := rtp.Elem()
|
||||
numFields := rt.NumField()
|
||||
reqFieldUsages := make([]string, 0, numFields)
|
||||
optFieldUsages := make([]string, 0, numFields)
|
||||
for i := 0; i < numFields; i++ {
|
||||
rtf := rt.Field(i)
|
||||
var isOptional bool
|
||||
if kind := rtf.Type.Kind(); kind == reflect.Ptr {
|
||||
isOptional = true
|
||||
}
|
||||
|
||||
var defaultVal *reflect.Value
|
||||
if defVal, ok := defaults[i]; ok {
|
||||
defaultVal = &defVal
|
||||
}
|
||||
|
||||
// Add human-readable usage to the appropriate slice that is
|
||||
// later used to generate the one-line usage.
|
||||
usage := fieldUsage(rtf, defaultVal)
|
||||
if isOptional {
|
||||
optFieldUsages = append(optFieldUsages, usage)
|
||||
} else {
|
||||
reqFieldUsages = append(reqFieldUsages, usage)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate and return the one-line usage string.
|
||||
usageStr := method
|
||||
if len(reqFieldUsages) > 0 {
|
||||
usageStr += " " + strings.Join(reqFieldUsages, " ")
|
||||
}
|
||||
if len(optFieldUsages) > 0 {
|
||||
usageStr += fmt.Sprintf(" (%s)", strings.Join(optFieldUsages, " "))
|
||||
}
|
||||
return usageStr
|
||||
}
|
||||
|
||||
// MethodUsageText returns a one-line usage string for the provided method. The
|
||||
// provided method must be associated with a registered type. All commands
|
||||
// provided by this package are registered by default.
|
||||
func MethodUsageText(method string) (string, error) {
|
||||
// Look up details about the provided method and error out if not
|
||||
// registered.
|
||||
registerLock.RLock()
|
||||
rtp, ok := methodToConcreteType[method]
|
||||
info := methodToInfo[method]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return "", makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
// When the usage for this method has already been generated, simply
|
||||
// return it.
|
||||
if info.usage != "" {
|
||||
return info.usage, nil
|
||||
}
|
||||
|
||||
// Generate and store the usage string for future calls and return it.
|
||||
usage := methodUsageText(rtp, info.defaults, method)
|
||||
registerLock.Lock()
|
||||
info.usage = usage
|
||||
methodToInfo[method] = info
|
||||
registerLock.Unlock()
|
||||
return usage, nil
|
||||
}
|
|
@ -0,0 +1,550 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// makeParams creates a slice of interface values for the given struct.
|
||||
func makeParams(rt reflect.Type, rv reflect.Value) []interface{} {
|
||||
numFields := rt.NumField()
|
||||
params := make([]interface{}, 0, numFields)
|
||||
for i := 0; i < numFields; i++ {
|
||||
rtf := rt.Field(i)
|
||||
rvf := rv.Field(i)
|
||||
if rtf.Type.Kind() == reflect.Ptr {
|
||||
if rvf.IsNil() {
|
||||
break
|
||||
}
|
||||
rvf.Elem()
|
||||
}
|
||||
params = append(params, rvf.Interface())
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
// MarshalCmd marshals the passed command to a JSON-RPC request byte slice that
|
||||
// is suitable for transmission to an RPC server. The provided command type
|
||||
// must be a registered type. All commands provided by this package are
|
||||
// registered by default.
|
||||
func MarshalCmd(id interface{}, cmd interface{}) ([]byte, error) {
|
||||
// Look up the cmd type and error out if not registered.
|
||||
rt := reflect.TypeOf(cmd)
|
||||
registerLock.RLock()
|
||||
method, ok := concreteTypeToMethod[rt]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return nil, makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
// The provided command must not be nil.
|
||||
rv := reflect.ValueOf(cmd)
|
||||
if rv.IsNil() {
|
||||
str := "the specified command is nil"
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Create a slice of interface values in the order of the struct fields
|
||||
// while respecting pointer fields as optional params and only adding
|
||||
// them if they are non-nil.
|
||||
params := makeParams(rt.Elem(), rv.Elem())
|
||||
|
||||
// Generate and marshal the final JSON-RPC request.
|
||||
rawCmd, err := NewRequest(id, method, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(rawCmd)
|
||||
}
|
||||
|
||||
// checkNumParams ensures the supplied number of params is at least the minimum
|
||||
// required number for the command and less than the maximum allowed.
|
||||
func checkNumParams(numParams int, info *methodInfo) error {
|
||||
if numParams < info.numReqParams || numParams > info.maxParams {
|
||||
if info.numReqParams == info.maxParams {
|
||||
str := fmt.Sprintf("wrong number of params (expected "+
|
||||
"%d, received %d)", info.numReqParams,
|
||||
numParams)
|
||||
return makeError(ErrNumParams, str)
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("wrong number of params (expected "+
|
||||
"between %d and %d, received %d)", info.numReqParams,
|
||||
info.maxParams, numParams)
|
||||
return makeError(ErrNumParams, str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// populateDefaults populates default values into any remaining optional struct
|
||||
// fields that did not have parameters explicitly provided. The caller should
|
||||
// have previously checked that the number of parameters being passed is at
|
||||
// least the required number of parameters to avoid unnecessary work in this
|
||||
// function, but since required fields never have default values, it will work
|
||||
// properly even without the check.
|
||||
func populateDefaults(numParams int, info *methodInfo, rv reflect.Value) {
|
||||
// When there are no more parameters left in the supplied parameters,
|
||||
// any remaining struct fields must be optional. Thus, populate them
|
||||
// with their associated default value as needed.
|
||||
for i := numParams; i < info.maxParams; i++ {
|
||||
rvf := rv.Field(i)
|
||||
if defaultVal, ok := info.defaults[i]; ok {
|
||||
rvf.Set(defaultVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalCmd unmarshals a JSON-RPC request into a suitable concrete command
|
||||
// so long as the method type contained within the marshalled request is
|
||||
// registered.
|
||||
func UnmarshalCmd(r *Request) (interface{}, error) {
|
||||
registerLock.RLock()
|
||||
rtp, ok := methodToConcreteType[r.Method]
|
||||
info := methodToInfo[r.Method]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", r.Method)
|
||||
return nil, makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
rt := rtp.Elem()
|
||||
rvp := reflect.New(rt)
|
||||
rv := rvp.Elem()
|
||||
|
||||
// Ensure the number of parameters are correct.
|
||||
numParams := len(r.Params)
|
||||
if err := checkNumParams(numParams, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Loop through each of the struct fields and unmarshal the associated
|
||||
// parameter into them.
|
||||
for i := 0; i < numParams; i++ {
|
||||
rvf := rv.Field(i)
|
||||
// Unmarshal the parameter into the struct field.
|
||||
concreteVal := rvf.Addr().Interface()
|
||||
if err := json.Unmarshal(r.Params[i], &concreteVal); err != nil {
|
||||
// The most common error is the wrong type, so
|
||||
// explicitly detect that error and make it nicer.
|
||||
fieldName := strings.ToLower(rt.Field(i).Name)
|
||||
if jerr, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"be type %v (got %v)", i+1, fieldName,
|
||||
jerr.Type, jerr.Value)
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Fallback to showing the underlying error.
|
||||
str := fmt.Sprintf("parameter #%d '%s' failed to "+
|
||||
"unmarshal: %v", i+1, fieldName, err)
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
}
|
||||
|
||||
// When there are less supplied parameters than the total number of
|
||||
// params, any remaining struct fields must be optional. Thus, populate
|
||||
// them with their associated default value as needed.
|
||||
if numParams < info.maxParams {
|
||||
populateDefaults(numParams, &info, rv)
|
||||
}
|
||||
|
||||
return rvp.Interface(), nil
|
||||
}
|
||||
|
||||
// isNumeric returns whether the passed reflect kind is a signed or unsigned
|
||||
// integer of any magnitude or a float of any magnitude.
|
||||
func isNumeric(kind reflect.Kind) bool {
|
||||
switch kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||
reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// typesMaybeCompatible returns whether the source type can possibly be
|
||||
// assigned to the destination type. This is intended as a relatively quick
|
||||
// check to weed out obviously invalid conversions.
|
||||
func typesMaybeCompatible(dest reflect.Type, src reflect.Type) bool {
|
||||
// The same types are obviously compatible.
|
||||
if dest == src {
|
||||
return true
|
||||
}
|
||||
|
||||
// When both types are numeric, they are potentially compatible.
|
||||
srcKind := src.Kind()
|
||||
destKind := dest.Kind()
|
||||
if isNumeric(destKind) && isNumeric(srcKind) {
|
||||
return true
|
||||
}
|
||||
|
||||
if srcKind == reflect.String {
|
||||
// Strings can potentially be converted to numeric types.
|
||||
if isNumeric(destKind) {
|
||||
return true
|
||||
}
|
||||
|
||||
switch destKind {
|
||||
// Strings can potentially be converted to bools by
|
||||
// strconv.ParseBool.
|
||||
case reflect.Bool:
|
||||
return true
|
||||
|
||||
// Strings can be converted to any other type which has as
|
||||
// underlying type of string.
|
||||
case reflect.String:
|
||||
return true
|
||||
|
||||
// Strings can potentially be converted to arrays, slice,
|
||||
// structs, and maps via json.Unmarshal.
|
||||
case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// baseType returns the type of the argument after indirecting through all
|
||||
// pointers along with how many indirections were necessary.
|
||||
func baseType(arg reflect.Type) (reflect.Type, int) {
|
||||
var numIndirects int
|
||||
for arg.Kind() == reflect.Ptr {
|
||||
arg = arg.Elem()
|
||||
numIndirects++
|
||||
}
|
||||
return arg, numIndirects
|
||||
}
|
||||
|
||||
// assignField is the main workhorse for the NewCmd function which handles
|
||||
// assigning the provided source value to the destination field. It supports
|
||||
// direct type assignments, indirection, conversion of numeric types, and
|
||||
// unmarshaling of strings into arrays, slices, structs, and maps via
|
||||
// json.Unmarshal.
|
||||
func assignField(paramNum int, fieldName string, dest reflect.Value, src reflect.Value) error {
|
||||
// Just error now when the types have no chance of being compatible.
|
||||
destBaseType, destIndirects := baseType(dest.Type())
|
||||
srcBaseType, srcIndirects := baseType(src.Type())
|
||||
if !typesMaybeCompatible(destBaseType, srcBaseType) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must be type %v (got "+
|
||||
"%v)", paramNum, fieldName, destBaseType, srcBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Check if it's possible to simply set the dest to the provided source.
|
||||
// This is the case when the base types are the same or they are both
|
||||
// pointers that can be indirected to be the same without needing to
|
||||
// create pointers for the destination field.
|
||||
if destBaseType == srcBaseType && srcIndirects >= destIndirects {
|
||||
for i := 0; i < srcIndirects-destIndirects; i++ {
|
||||
src = src.Elem()
|
||||
}
|
||||
dest.Set(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
// When the destination has more indirects than the source, the extra
|
||||
// pointers have to be created. Only create enough pointers to reach
|
||||
// the same level of indirection as the source so the dest can simply be
|
||||
// set to the provided source when the types are the same.
|
||||
destIndirectsRemaining := destIndirects
|
||||
if destIndirects > srcIndirects {
|
||||
indirectDiff := destIndirects - srcIndirects
|
||||
for i := 0; i < indirectDiff; i++ {
|
||||
dest.Set(reflect.New(dest.Type().Elem()))
|
||||
dest = dest.Elem()
|
||||
destIndirectsRemaining--
|
||||
}
|
||||
}
|
||||
|
||||
if destBaseType == srcBaseType {
|
||||
dest.Set(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make any remaining pointers needed to get to the base dest type since
|
||||
// the above direct assign was not possible and conversions are done
|
||||
// against the base types.
|
||||
for i := 0; i < destIndirectsRemaining; i++ {
|
||||
dest.Set(reflect.New(dest.Type().Elem()))
|
||||
dest = dest.Elem()
|
||||
}
|
||||
|
||||
// Indirect through to the base source value.
|
||||
for src.Kind() == reflect.Ptr {
|
||||
src = src.Elem()
|
||||
}
|
||||
|
||||
// Perform supported type conversions.
|
||||
switch src.Kind() {
|
||||
// Source value is a signed integer of various magnitude.
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||
reflect.Int64:
|
||||
|
||||
switch dest.Kind() {
|
||||
// Destination is a signed integer of various magnitude.
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||
reflect.Int64:
|
||||
|
||||
srcInt := src.Int()
|
||||
if dest.OverflowInt(srcInt) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
dest.SetInt(srcInt)
|
||||
|
||||
// Destination is an unsigned integer of various magnitude.
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
|
||||
srcInt := src.Int()
|
||||
if srcInt < 0 || dest.OverflowUint(uint64(srcInt)) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetUint(uint64(srcInt))
|
||||
|
||||
default:
|
||||
str := fmt.Sprintf("parameter #%d '%s' must be type "+
|
||||
"%v (got %v)", paramNum, fieldName, destBaseType,
|
||||
srcBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Source value is an unsigned integer of various magnitude.
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
|
||||
switch dest.Kind() {
|
||||
// Destination is a signed integer of various magnitude.
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||
reflect.Int64:
|
||||
|
||||
srcUint := src.Uint()
|
||||
if srcUint > uint64(1<<63)-1 {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
if dest.OverflowInt(int64(srcUint)) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetInt(int64(srcUint))
|
||||
|
||||
// Destination is an unsigned integer of various magnitude.
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||
reflect.Uint64:
|
||||
|
||||
srcUint := src.Uint()
|
||||
if dest.OverflowUint(srcUint) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetUint(srcUint)
|
||||
|
||||
default:
|
||||
str := fmt.Sprintf("parameter #%d '%s' must be type "+
|
||||
"%v (got %v)", paramNum, fieldName, destBaseType,
|
||||
srcBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Source value is a float.
|
||||
case reflect.Float32, reflect.Float64:
|
||||
destKind := dest.Kind()
|
||||
if destKind != reflect.Float32 && destKind != reflect.Float64 {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must be type "+
|
||||
"%v (got %v)", paramNum, fieldName, destBaseType,
|
||||
srcBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
srcFloat := src.Float()
|
||||
if dest.OverflowFloat(srcFloat) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' overflows "+
|
||||
"destination type %v", paramNum, fieldName,
|
||||
destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetFloat(srcFloat)
|
||||
|
||||
// Source value is a string.
|
||||
case reflect.String:
|
||||
switch dest.Kind() {
|
||||
// String -> bool
|
||||
case reflect.Bool:
|
||||
b, err := strconv.ParseBool(src.String())
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"parse to a %v", paramNum, fieldName,
|
||||
destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetBool(b)
|
||||
|
||||
// String -> signed integer of varying size.
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||
reflect.Int64:
|
||||
|
||||
srcInt, err := strconv.ParseInt(src.String(), 0, 0)
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"parse to a %v", paramNum, fieldName,
|
||||
destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
if dest.OverflowInt(srcInt) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetInt(srcInt)
|
||||
|
||||
// String -> unsigned integer of varying size.
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64:
|
||||
|
||||
srcUint, err := strconv.ParseUint(src.String(), 0, 0)
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"parse to a %v", paramNum, fieldName,
|
||||
destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
if dest.OverflowUint(srcUint) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetUint(srcUint)
|
||||
|
||||
// String -> float of varying size.
|
||||
case reflect.Float32, reflect.Float64:
|
||||
srcFloat, err := strconv.ParseFloat(src.String(), 0)
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"parse to a %v", paramNum, fieldName,
|
||||
destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
if dest.OverflowFloat(srcFloat) {
|
||||
str := fmt.Sprintf("parameter #%d '%s' "+
|
||||
"overflows destination type %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.SetFloat(srcFloat)
|
||||
|
||||
// String -> string (typecast).
|
||||
case reflect.String:
|
||||
dest.SetString(src.String())
|
||||
|
||||
// String -> arrays, slices, structs, and maps via
|
||||
// json.Unmarshal.
|
||||
case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
|
||||
concreteVal := dest.Addr().Interface()
|
||||
err := json.Unmarshal([]byte(src.String()), &concreteVal)
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("parameter #%d '%s' must "+
|
||||
"be valid JSON which unsmarshals to a %v",
|
||||
paramNum, fieldName, destBaseType)
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
dest.Set(reflect.ValueOf(concreteVal).Elem())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmd provides a generic mechanism to create a new command that can marshal
|
||||
// to a JSON-RPC request while respecting the requirements of the provided
|
||||
// method. The method must have been registered with the package already along
|
||||
// with its type definition. All methods associated with the commands exported
|
||||
// by this package are already registered by default.
|
||||
//
|
||||
// The arguments are most efficient when they are the exact same type as the
|
||||
// underlying field in the command struct associated with the the method,
|
||||
// however this function also will perform a variety of conversions to make it
|
||||
// more flexible. This allows, for example, command line args which are strings
|
||||
// to be passed unaltered. In particular, the following conversions are
|
||||
// supported:
|
||||
//
|
||||
// - Conversion between any size signed or unsigned integer so long as the
|
||||
// value does not overflow the destination type
|
||||
// - Conversion between float32 and float64 so long as the value does not
|
||||
// overflow the destination type
|
||||
// - Conversion from string to boolean for everything strconv.ParseBool
|
||||
// recognizes
|
||||
// - Conversion from string to any size integer for everything
|
||||
// strconv.ParseInt and strconv.ParseUint recognizes
|
||||
// - Conversion from string to any size float for everything
|
||||
// strconv.ParseFloat recognizes
|
||||
// - Conversion from string to arrays, slices, structs, and maps by treating
|
||||
// the string as marshalled JSON and calling json.Unmarshal into the
|
||||
// destination field
|
||||
func NewCmd(method string, args ...interface{}) (interface{}, error) {
|
||||
// Look up details about the provided method. Any methods that aren't
|
||||
// registered are an error.
|
||||
registerLock.RLock()
|
||||
rtp, ok := methodToConcreteType[method]
|
||||
info := methodToInfo[method]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return nil, makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
// Ensure the number of parameters are correct.
|
||||
numParams := len(args)
|
||||
if err := checkNumParams(numParams, &info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the appropriate command type for the method. Since all types
|
||||
// are enforced to be a pointer to a struct at registration time, it's
|
||||
// safe to indirect to the struct now.
|
||||
rvp := reflect.New(rtp.Elem())
|
||||
rv := rvp.Elem()
|
||||
rt := rtp.Elem()
|
||||
|
||||
// Loop through each of the struct fields and assign the associated
|
||||
// parameter into them after checking its type validity.
|
||||
for i := 0; i < numParams; i++ {
|
||||
// Attempt to assign each of the arguments to the according
|
||||
// struct field.
|
||||
rvf := rv.Field(i)
|
||||
fieldName := strings.ToLower(rt.Field(i).Name)
|
||||
err := assignField(i+1, fieldName, rvf, reflect.ValueOf(args[i]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return rvp.Interface(), nil
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package btcjson provides primitives for working with the bitcoin JSON-RPC API.
|
||||
|
||||
Overview
|
||||
|
||||
When communicating via the JSON-RPC protocol, all of the commands need to be
|
||||
marshalled to and from the the wire in the appropriate format. This package
|
||||
provides data structures and primitives to ease this process.
|
||||
|
||||
In addition, it also provides some additional features such as custom command
|
||||
registration, command categorization, and reflection-based help generation.
|
||||
|
||||
JSON-RPC Protocol Overview
|
||||
|
||||
This information is not necessary in order to use this package, but it does
|
||||
provide some intuition into what the marshalling and unmarshalling that is
|
||||
discussed below is doing under the hood.
|
||||
|
||||
As defined by the JSON-RPC spec, there are effectively two forms of messages on
|
||||
the wire:
|
||||
|
||||
- Request Objects
|
||||
{"jsonrpc":"1.0","id":"SOMEID","method":"SOMEMETHOD","params":[SOMEPARAMS]}
|
||||
NOTE: Notifications are the same format except the id field is null.
|
||||
|
||||
- Response Objects
|
||||
{"result":SOMETHING,"error":null,"id":"SOMEID"}
|
||||
{"result":null,"error":{"code":SOMEINT,"message":SOMESTRING},"id":"SOMEID"}
|
||||
|
||||
For requests, the params field can vary in what it contains depending on the
|
||||
method (a.k.a. command) being sent. Each parameter can be as simple as an int
|
||||
or a complex structure containing many nested fields. The id field is used to
|
||||
identify a request and will be included in the associated response.
|
||||
|
||||
When working with asynchronous transports, such as websockets, spontaneous
|
||||
notifications are also possible. As indicated, they are the same as a request
|
||||
object, except they have the id field set to null. Therefore, servers will
|
||||
ignore requests with the id field set to null, while clients can choose to
|
||||
consume or ignore them.
|
||||
|
||||
Unfortunately, the original Bitcoin JSON-RPC API (and hence anything compatible
|
||||
with it) doesn't always follow the spec and will sometimes return an error
|
||||
string in the result field with a null error for certain commands. However,
|
||||
for the most part, the error field will be set as described on failure.
|
||||
|
||||
Marshalling and Unmarshalling
|
||||
|
||||
Based upon the discussion above, it should be easy to see how the types of this
|
||||
package map into the required parts of the protocol
|
||||
|
||||
- Request Objects (type Request)
|
||||
- Commands (type <Foo>Cmd)
|
||||
- Notifications (type <Foo>Ntfn)
|
||||
- Response Objects (type Response)
|
||||
- Result (type <Foo>Result)
|
||||
|
||||
To simplify the marshalling of the requests and responses, the MarshalCmd and
|
||||
MarshalResponse functions are provided. They return the raw bytes ready to be
|
||||
sent across the wire.
|
||||
|
||||
Unmarshalling a received Request object is a two step process:
|
||||
1) Unmarshal the raw bytes into a Request struct instance via json.Unmarshal
|
||||
2) Use UnmarshalCmd on the Result field of the unmarshalled Request to create
|
||||
a concrete command or notification instance with all struct fields set
|
||||
accordingly
|
||||
|
||||
This approach is used since it provides the caller with access to the additional
|
||||
fields in the request that are not part of the command such as the ID.
|
||||
|
||||
Unmarshalling a received Response object is also a two step process:
|
||||
1) Unmarhsal the raw bytes into a Response struct instance via json.Unmarshal
|
||||
2) Depending on the ID, unmarshal the Result field of the unmarshalled
|
||||
Response to create a concrete type instance
|
||||
|
||||
As above, this approach is used since it provides the caller with access to the
|
||||
fields in the response such as the ID and Error.
|
||||
|
||||
Command Creation
|
||||
|
||||
This package provides two approaches for creating a new command. This first,
|
||||
and preferred, method is to use one of the New<Foo>Cmd functions. This allows
|
||||
static compile-time checking to help ensure the parameters stay in sync with
|
||||
the struct definitions.
|
||||
|
||||
The second approach is the NewCmd function which takes a method (command) name
|
||||
and variable arguments. The function includes full checking to ensure the
|
||||
parameters are accurate according to provided method, however these checks are,
|
||||
obviously, run-time which means any mistakes won't be found until the code is
|
||||
actually executed. However, it is quite useful for user-supplied commands
|
||||
that are intentionally dynamic.
|
||||
|
||||
Custom Command Registration
|
||||
|
||||
The command handling of this package is built around the concept of registered
|
||||
commands. This is true for the wide variety of commands already provided by the
|
||||
package, but it also means caller can easily provide custom commands with all
|
||||
of the same functionality as the built-in commands. Use the RegisterCmd
|
||||
function for this purpose.
|
||||
|
||||
A list of all registered methods can be obtained with the RegisteredCmdMethods
|
||||
function.
|
||||
|
||||
Command Inspection
|
||||
|
||||
All registered commands are registered with flags that identify information such
|
||||
as whether the command applies to a chain server, wallet server, or is a
|
||||
notification along with the method name to use. These flags can be obtained
|
||||
with the MethodUsageFlags flags, and the method can be obtained with the
|
||||
CmdMethod function.
|
||||
|
||||
Help Generation
|
||||
|
||||
To facilitate providing consistent help to users of the RPC server, this package
|
||||
exposes the GenerateHelp and function which uses reflection on registered
|
||||
commands or notifications, as well as the provided expected result types, to
|
||||
generate the final help text.
|
||||
|
||||
In addition, the MethodUsageText function is provided to generate consistent
|
||||
one-line usage for registered commands and notifications using reflection.
|
||||
|
||||
Errors
|
||||
|
||||
There are 2 distinct type of errors supported by this package:
|
||||
|
||||
- General errors related to marshalling or unmarshalling or improper use of
|
||||
the package (type Error)
|
||||
- RPC errors which are intended to be returned across the wire as a part of
|
||||
the JSON-RPC response (type RPCError)
|
||||
|
||||
The first category of errors (type Error) typically indicates a programmer error
|
||||
and can be avoided by properly using the API. Errors of this type will be
|
||||
returned from the various functions available in this package. They identify
|
||||
issues such as unsupported field types, attempts to register malformed commands,
|
||||
and attempting to create a new command with an improper number of parameters.
|
||||
The specific reason for the error can be detected by type asserting it to a
|
||||
*btcjson.Error and accessing the ErrorCode field.
|
||||
|
||||
The second category of errors (type RPCError), on the other hand, are useful for
|
||||
returning errors to RPC clients. Consequently, they are used in the previously
|
||||
described Response type.
|
||||
*/
|
||||
package btcjson
|
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrorCode identifies a kind of error. These error codes are NOT used for
|
||||
// JSON-RPC response errors.
|
||||
type ErrorCode int
|
||||
|
||||
// These constants are used to identify a specific RuleError.
|
||||
const (
|
||||
// ErrDuplicateMethod indicates a command with the specified method
|
||||
// already exists.
|
||||
ErrDuplicateMethod ErrorCode = iota
|
||||
|
||||
// ErrInvalidUsageFlags indicates one or more unrecognized flag bits
|
||||
// were specified.
|
||||
ErrInvalidUsageFlags
|
||||
|
||||
// ErrInvalidType indicates a type was passed that is not the required
|
||||
// type.
|
||||
ErrInvalidType
|
||||
|
||||
// ErrEmbeddedType indicates the provided command struct contains an
|
||||
// embedded type which is not not supported.
|
||||
ErrEmbeddedType
|
||||
|
||||
// ErrUnexportedField indiciates the provided command struct contains an
|
||||
// unexported field which is not supported.
|
||||
ErrUnexportedField
|
||||
|
||||
// ErrUnsupportedFieldType indicates the type of a field in the provided
|
||||
// command struct is not one of the supported types.
|
||||
ErrUnsupportedFieldType
|
||||
|
||||
// ErrNonOptionalField indicates a non-optional field was specified
|
||||
// after an optional field.
|
||||
ErrNonOptionalField
|
||||
|
||||
// ErrNonOptionalDefault indicates a 'jsonrpcdefault' struct tag was
|
||||
// specified for a non-optional field.
|
||||
ErrNonOptionalDefault
|
||||
|
||||
// ErrMismatchedDefault indicates a 'jsonrpcdefault' struct tag contains
|
||||
// a value that doesn't match the type of the field.
|
||||
ErrMismatchedDefault
|
||||
|
||||
// ErrUnregisteredMethod indicates a method was specified that has not
|
||||
// been registered.
|
||||
ErrUnregisteredMethod
|
||||
|
||||
// ErrMissingDescription indicates a description required to generate
|
||||
// help is missing.
|
||||
ErrMissingDescription
|
||||
|
||||
// ErrNumParams inidcates the number of params supplied do not
|
||||
// match the requirements of the associated command.
|
||||
ErrNumParams
|
||||
|
||||
// numErrorCodes is the maximum error code number used in tests.
|
||||
numErrorCodes
|
||||
)
|
||||
|
||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||
var errorCodeStrings = map[ErrorCode]string{
|
||||
ErrDuplicateMethod: "ErrDuplicateMethod",
|
||||
ErrInvalidUsageFlags: "ErrInvalidUsageFlags",
|
||||
ErrInvalidType: "ErrInvalidType",
|
||||
ErrEmbeddedType: "ErrEmbeddedType",
|
||||
ErrUnexportedField: "ErrUnexportedField",
|
||||
ErrUnsupportedFieldType: "ErrUnsupportedFieldType",
|
||||
ErrNonOptionalField: "ErrNonOptionalField",
|
||||
ErrNonOptionalDefault: "ErrNonOptionalDefault",
|
||||
ErrMismatchedDefault: "ErrMismatchedDefault",
|
||||
ErrUnregisteredMethod: "ErrUnregisteredMethod",
|
||||
ErrMissingDescription: "ErrMissingDescription",
|
||||
ErrNumParams: "ErrNumParams",
|
||||
}
|
||||
|
||||
// String returns the ErrorCode as a human-readable name.
|
||||
func (e ErrorCode) String() string {
|
||||
if s := errorCodeStrings[e]; s != "" {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
|
||||
}
|
||||
|
||||
// Error identifies a general error. This differs from an RPCError in that this
|
||||
// error typically is used more by the consumers of the package as opposed to
|
||||
// RPCErrors which are intended to be returned to the client across the wire via
|
||||
// a JSON-RPC Response. The caller can use type assertions to determine the
|
||||
// specific error and access the ErrorCode field.
|
||||
type Error struct {
|
||||
ErrorCode ErrorCode // Describes the kind of error
|
||||
Description string // Human readable description of the issue
|
||||
}
|
||||
|
||||
// Error satisfies the error interface and prints human-readable errors.
|
||||
func (e Error) Error() string {
|
||||
return e.Description
|
||||
}
|
||||
|
||||
// makeError creates an Error given a set of arguments.
|
||||
func makeError(c ErrorCode, desc string) Error {
|
||||
return Error{ErrorCode: c, Description: desc}
|
||||
}
|
|
@ -0,0 +1,560 @@
|
|||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
// baseHelpDescs house the various help labels, types, and example values used
|
||||
// when generating help. The per-command synopsis, field descriptions,
|
||||
// conditions, and result descriptions are to be provided by the caller.
|
||||
var baseHelpDescs = map[string]string{
|
||||
// Misc help labels and output.
|
||||
"help-arguments": "Arguments",
|
||||
"help-arguments-none": "None",
|
||||
"help-result": "Result",
|
||||
"help-result-nothing": "Nothing",
|
||||
"help-default": "default",
|
||||
"help-optional": "optional",
|
||||
"help-required": "required",
|
||||
|
||||
// JSON types.
|
||||
"json-type-numeric": "numeric",
|
||||
"json-type-string": "string",
|
||||
"json-type-bool": "boolean",
|
||||
"json-type-array": "array of ",
|
||||
"json-type-object": "object",
|
||||
"json-type-value": "value",
|
||||
|
||||
// JSON examples.
|
||||
"json-example-string": "value",
|
||||
"json-example-bool": "true|false",
|
||||
"json-example-map-data": "data",
|
||||
"json-example-unknown": "unknown",
|
||||
}
|
||||
|
||||
// descLookupFunc is a function which is used to lookup a description given
|
||||
// a key.
|
||||
type descLookupFunc func(string) string
|
||||
|
||||
// reflectTypeToJSONType returns a string that represents the JSON type
|
||||
// associated with the provided Go type.
|
||||
func reflectTypeToJSONType(xT descLookupFunc, rt reflect.Type) string {
|
||||
kind := rt.Kind()
|
||||
if isNumeric(kind) {
|
||||
return xT("json-type-numeric")
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return xT("json-type-string")
|
||||
|
||||
case reflect.Bool:
|
||||
return xT("json-type-bool")
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
return xT("json-type-array") + reflectTypeToJSONType(xT,
|
||||
rt.Elem())
|
||||
|
||||
case reflect.Struct:
|
||||
return xT("json-type-object")
|
||||
|
||||
case reflect.Map:
|
||||
return xT("json-type-object")
|
||||
}
|
||||
|
||||
return xT("json-type-value")
|
||||
}
|
||||
|
||||
// resultStructHelp returns a slice of strings containing the result help output
|
||||
// for a struct. Each line makes use of tabs to separate the relevant pieces so
|
||||
// a tabwriter can be used later to line everything up. The descriptions are
|
||||
// pulled from the active help descriptions map based on the lowercase version
|
||||
// of the provided reflect type and json name (or the lowercase version of the
|
||||
// field name if no json tag was specified).
|
||||
func resultStructHelp(xT descLookupFunc, rt reflect.Type, indentLevel int) []string {
|
||||
indent := strings.Repeat(" ", indentLevel)
|
||||
typeName := strings.ToLower(rt.Name())
|
||||
|
||||
// Generate the help for each of the fields in the result struct.
|
||||
numField := rt.NumField()
|
||||
results := make([]string, 0, numField)
|
||||
for i := 0; i < numField; i++ {
|
||||
rtf := rt.Field(i)
|
||||
|
||||
// The field name to display is the json name when it's
|
||||
// available, otherwise use the lowercase field name.
|
||||
var fieldName string
|
||||
if tag := rtf.Tag.Get("json"); tag != "" {
|
||||
fieldName = strings.Split(tag, ",")[0]
|
||||
} else {
|
||||
fieldName = strings.ToLower(rtf.Name)
|
||||
}
|
||||
|
||||
// Deference pointer if needed.
|
||||
rtfType := rtf.Type
|
||||
if rtfType.Kind() == reflect.Ptr {
|
||||
rtfType = rtf.Type.Elem()
|
||||
}
|
||||
|
||||
// Generate the JSON example for the result type of this struct
|
||||
// field. When it is a complex type, examine the type and
|
||||
// adjust the opening bracket and brace combination accordingly.
|
||||
fieldType := reflectTypeToJSONType(xT, rtfType)
|
||||
fieldDescKey := typeName + "-" + fieldName
|
||||
fieldExamples, isComplex := reflectTypeToJSONExample(xT,
|
||||
rtfType, indentLevel, fieldDescKey)
|
||||
if isComplex {
|
||||
var brace string
|
||||
kind := rtfType.Kind()
|
||||
if kind == reflect.Array || kind == reflect.Slice {
|
||||
brace = "[{"
|
||||
} else {
|
||||
brace = "{"
|
||||
}
|
||||
result := fmt.Sprintf("%s\"%s\": %s\t(%s)\t%s", indent,
|
||||
fieldName, brace, fieldType, xT(fieldDescKey))
|
||||
results = append(results, result)
|
||||
results = append(results, fieldExamples...)
|
||||
} else {
|
||||
result := fmt.Sprintf("%s\"%s\": %s,\t(%s)\t%s", indent,
|
||||
fieldName, fieldExamples[0], fieldType,
|
||||
xT(fieldDescKey))
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// reflectTypeToJSONExample generates example usage in the format used by the
|
||||
// help output. It handles arrays, slices and structs recursively. The output
|
||||
// is returned as a slice of lines so the final help can be nicely aligned via
|
||||
// a tab writer. A bool is also returned which specifies whether or not the
|
||||
// type results in a complex JSON object since they need to be handled
|
||||
// differently.
|
||||
func reflectTypeToJSONExample(xT descLookupFunc, rt reflect.Type, indentLevel int, fieldDescKey string) ([]string, bool) {
|
||||
// Indirect pointer if needed.
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
}
|
||||
kind := rt.Kind()
|
||||
if isNumeric(kind) {
|
||||
if kind == reflect.Float32 || kind == reflect.Float64 {
|
||||
return []string{"n.nnn"}, false
|
||||
}
|
||||
|
||||
return []string{"n"}, false
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.String:
|
||||
return []string{`"` + xT("json-example-string") + `"`}, false
|
||||
|
||||
case reflect.Bool:
|
||||
return []string{xT("json-example-bool")}, false
|
||||
|
||||
case reflect.Struct:
|
||||
indent := strings.Repeat(" ", indentLevel)
|
||||
results := resultStructHelp(xT, rt, indentLevel+1)
|
||||
|
||||
// An opening brace is needed for the first indent level. For
|
||||
// all others, it will be included as a part of the previous
|
||||
// field.
|
||||
if indentLevel == 0 {
|
||||
newResults := make([]string, len(results)+1)
|
||||
newResults[0] = "{"
|
||||
copy(newResults[1:], results)
|
||||
results = newResults
|
||||
}
|
||||
|
||||
// The closing brace has a comma after it except for the first
|
||||
// indent level. The final tabs are necessary so the tab writer
|
||||
// lines things up properly.
|
||||
closingBrace := indent + "}"
|
||||
if indentLevel > 0 {
|
||||
closingBrace += ","
|
||||
}
|
||||
results = append(results, closingBrace+"\t\t")
|
||||
return results, true
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
results, isComplex := reflectTypeToJSONExample(xT, rt.Elem(),
|
||||
indentLevel, fieldDescKey)
|
||||
|
||||
// When the result is complex, it is because this is an array of
|
||||
// objects.
|
||||
if isComplex {
|
||||
// When this is at indent level zero, there is no
|
||||
// previous field to house the opening array bracket, so
|
||||
// replace the opening object brace with the array
|
||||
// syntax. Also, replace the final closing object brace
|
||||
// with the variadiac array closing syntax.
|
||||
indent := strings.Repeat(" ", indentLevel)
|
||||
if indentLevel == 0 {
|
||||
results[0] = indent + "[{"
|
||||
results[len(results)-1] = indent + "},...]"
|
||||
return results, true
|
||||
}
|
||||
|
||||
// At this point, the indent level is greater than 0, so
|
||||
// the opening array bracket and object brace are
|
||||
// already a part of the previous field. However, the
|
||||
// closing entry is a simple object brace, so replace it
|
||||
// with the variadiac array closing syntax. The final
|
||||
// tabs are necessary so the tab writer lines things up
|
||||
// properly.
|
||||
results[len(results)-1] = indent + "},...],\t\t"
|
||||
return results, true
|
||||
}
|
||||
|
||||
// It's an array of primitives, so return the formatted text
|
||||
// accordingly.
|
||||
return []string{fmt.Sprintf("[%s,...]", results[0])}, false
|
||||
|
||||
case reflect.Map:
|
||||
indent := strings.Repeat(" ", indentLevel)
|
||||
results := make([]string, 0, 3)
|
||||
|
||||
// An opening brace is needed for the first indent level. For
|
||||
// all others, it will be included as a part of the previous
|
||||
// field.
|
||||
if indentLevel == 0 {
|
||||
results = append(results, indent+"{")
|
||||
}
|
||||
|
||||
// Maps are a bit special in that they need to have the key,
|
||||
// value, and description of the object entry specifically
|
||||
// called out.
|
||||
innerIndent := strings.Repeat(" ", indentLevel+1)
|
||||
result := fmt.Sprintf("%s%q: %s, (%s) %s", innerIndent,
|
||||
xT(fieldDescKey+"--key"), xT(fieldDescKey+"--value"),
|
||||
reflectTypeToJSONType(xT, rt), xT(fieldDescKey+"--desc"))
|
||||
results = append(results, result)
|
||||
results = append(results, innerIndent+"...")
|
||||
|
||||
results = append(results, indent+"}")
|
||||
return results, true
|
||||
}
|
||||
|
||||
return []string{xT("json-example-unknown")}, false
|
||||
}
|
||||
|
||||
// resultTypeHelp generates and returns formatted help for the provided result
|
||||
// type.
|
||||
func resultTypeHelp(xT descLookupFunc, rt reflect.Type, fieldDescKey string) string {
|
||||
// Generate the JSON example for the result type.
|
||||
results, isComplex := reflectTypeToJSONExample(xT, rt, 0, fieldDescKey)
|
||||
|
||||
// When this is a primitive type, add the associated JSON type and
|
||||
// result description into the final string, format it accordingly,
|
||||
// and return it.
|
||||
if !isComplex {
|
||||
return fmt.Sprintf("%s (%s) %s", results[0],
|
||||
reflectTypeToJSONType(xT, rt), xT(fieldDescKey))
|
||||
}
|
||||
|
||||
// At this point, this is a complex type that already has the JSON types
|
||||
// and descriptions in the results. Thus, use a tab writer to nicely
|
||||
// align the help text.
|
||||
var formatted bytes.Buffer
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(&formatted, 0, 4, 1, ' ', 0)
|
||||
for i, text := range results {
|
||||
if i == len(results)-1 {
|
||||
fmt.Fprintf(w, text)
|
||||
} else {
|
||||
fmt.Fprintln(w, text)
|
||||
}
|
||||
}
|
||||
w.Flush()
|
||||
return formatted.String()
|
||||
}
|
||||
|
||||
// argTypeHelp returns the type of provided command argument as a string in the
|
||||
// format used by the help output. In particular, it includes the JSON type
|
||||
// (boolean, numeric, string, array, object) along with optional and the default
|
||||
// value if applicable.
|
||||
func argTypeHelp(xT descLookupFunc, structField reflect.StructField, defaultVal *reflect.Value) string {
|
||||
// Indirect the pointer if needed and track if it's an optional field.
|
||||
fieldType := structField.Type
|
||||
var isOptional bool
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
isOptional = true
|
||||
}
|
||||
|
||||
// When there is a default value, it must also be a pointer due to the
|
||||
// rules enforced by RegisterCmd.
|
||||
if defaultVal != nil {
|
||||
indirect := defaultVal.Elem()
|
||||
defaultVal = &indirect
|
||||
}
|
||||
|
||||
// Convert the field type to a JSON type.
|
||||
details := make([]string, 0, 3)
|
||||
details = append(details, reflectTypeToJSONType(xT, fieldType))
|
||||
|
||||
// Add optional and default value to the details if needed.
|
||||
if isOptional {
|
||||
details = append(details, xT("help-optional"))
|
||||
|
||||
// Add the default value if there is one. This is only checked
|
||||
// when the field is optional since a non-optional field can't
|
||||
// have a default value.
|
||||
if defaultVal != nil {
|
||||
val := defaultVal.Interface()
|
||||
if defaultVal.Kind() == reflect.String {
|
||||
val = fmt.Sprintf(`"%s"`, val)
|
||||
}
|
||||
str := fmt.Sprintf("%s=%v", xT("help-default"), val)
|
||||
details = append(details, str)
|
||||
}
|
||||
} else {
|
||||
details = append(details, xT("help-required"))
|
||||
}
|
||||
|
||||
return strings.Join(details, ", ")
|
||||
}
|
||||
|
||||
// argHelp generates and returns formatted help for the provided command.
|
||||
func argHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string) string {
|
||||
// Return now if the command has no arguments.
|
||||
rt := rtp.Elem()
|
||||
numFields := rt.NumField()
|
||||
if numFields == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Generate the help for each argument in the command. Several
|
||||
// simplifying assumptions are made here because the RegisterCmd
|
||||
// function has already rigorously enforced the layout.
|
||||
args := make([]string, 0, numFields)
|
||||
for i := 0; i < numFields; i++ {
|
||||
rtf := rt.Field(i)
|
||||
var defaultVal *reflect.Value
|
||||
if defVal, ok := defaults[i]; ok {
|
||||
defaultVal = &defVal
|
||||
}
|
||||
|
||||
fieldName := strings.ToLower(rtf.Name)
|
||||
helpText := fmt.Sprintf("%d.\t%s\t(%s)\t%s", i+1, fieldName,
|
||||
argTypeHelp(xT, rtf, defaultVal),
|
||||
xT(method+"-"+fieldName))
|
||||
args = append(args, helpText)
|
||||
|
||||
// For types which require a JSON object, or an array of JSON
|
||||
// objects, generate the full syntax for the argument.
|
||||
fieldType := rtf.Type
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
}
|
||||
kind := fieldType.Kind()
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName)
|
||||
resultText := resultTypeHelp(xT, fieldType, fieldDescKey)
|
||||
args = append(args, resultText)
|
||||
|
||||
case reflect.Map:
|
||||
fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName)
|
||||
resultText := resultTypeHelp(xT, fieldType, fieldDescKey)
|
||||
args = append(args, resultText)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
fieldDescKey := fmt.Sprintf("%s-%s", method, fieldName)
|
||||
if rtf.Type.Elem().Kind() == reflect.Struct {
|
||||
resultText := resultTypeHelp(xT, fieldType,
|
||||
fieldDescKey)
|
||||
args = append(args, resultText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add argument names, types, and descriptions if there are any. Use a
|
||||
// tab writer to nicely align the help text.
|
||||
var formatted bytes.Buffer
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(&formatted, 0, 4, 1, ' ', 0)
|
||||
for _, text := range args {
|
||||
fmt.Fprintln(w, text)
|
||||
}
|
||||
w.Flush()
|
||||
return formatted.String()
|
||||
}
|
||||
|
||||
// methodHelp generates and returns the help output for the provided command
|
||||
// and method info. This is the main work horse for the exported MethodHelp
|
||||
// function.
|
||||
func methodHelp(xT descLookupFunc, rtp reflect.Type, defaults map[int]reflect.Value, method string, resultTypes []interface{}) string {
|
||||
// Start off with the method usage and help synopsis.
|
||||
help := fmt.Sprintf("%s\n\n%s\n", methodUsageText(rtp, defaults, method),
|
||||
xT(method+"--synopsis"))
|
||||
|
||||
// Generate the help for each argument in the command.
|
||||
if argText := argHelp(xT, rtp, defaults, method); argText != "" {
|
||||
help += fmt.Sprintf("\n%s:\n%s", xT("help-arguments"),
|
||||
argText)
|
||||
} else {
|
||||
help += fmt.Sprintf("\n%s:\n%s\n", xT("help-arguments"),
|
||||
xT("help-arguments-none"))
|
||||
}
|
||||
|
||||
// Generate the help text for each result type.
|
||||
resultTexts := make([]string, 0, len(resultTypes))
|
||||
for i := range resultTypes {
|
||||
rtp := reflect.TypeOf(resultTypes[i])
|
||||
fieldDescKey := fmt.Sprintf("%s--result%d", method, i)
|
||||
if resultTypes[i] == nil {
|
||||
resultText := xT("help-result-nothing")
|
||||
resultTexts = append(resultTexts, resultText)
|
||||
continue
|
||||
}
|
||||
|
||||
resultText := resultTypeHelp(xT, rtp.Elem(), fieldDescKey)
|
||||
resultTexts = append(resultTexts, resultText)
|
||||
}
|
||||
|
||||
// Add result types and descriptions. When there is more than one
|
||||
// result type, also add the condition which triggers it.
|
||||
if len(resultTexts) > 1 {
|
||||
for i, resultText := range resultTexts {
|
||||
condKey := fmt.Sprintf("%s--condition%d", method, i)
|
||||
help += fmt.Sprintf("\n%s (%s):\n%s\n",
|
||||
xT("help-result"), xT(condKey), resultText)
|
||||
}
|
||||
} else if len(resultTexts) > 0 {
|
||||
help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"),
|
||||
resultTexts[0])
|
||||
} else {
|
||||
help += fmt.Sprintf("\n%s:\n%s\n", xT("help-result"),
|
||||
xT("help-result-nothing"))
|
||||
}
|
||||
return help
|
||||
}
|
||||
|
||||
// isValidResultType returns whether the passed reflect kind is one of the
|
||||
// acceptable types for results.
|
||||
func isValidResultType(kind reflect.Kind) bool {
|
||||
if isNumeric(kind) {
|
||||
return true
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.String, reflect.Struct, reflect.Array, reflect.Slice,
|
||||
reflect.Bool, reflect.Map:
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GenerateHelp generates and returns help output for the provided method and
|
||||
// result types given a map to provide the appropriate keys for the method
|
||||
// synopsis, field descriptions, conditions, and result descriptions. The
|
||||
// method must be associated with a registered type. All commands provided by
|
||||
// this package are registered by default.
|
||||
//
|
||||
// The resultTypes must be pointer-to-types which represent the specific types
|
||||
// of values the command returns. For example, if the command only returns a
|
||||
// boolean value, there should only be a single entry of (*bool)(nil). Note
|
||||
// that each type must be a single pointer to the type. Therefore, it is
|
||||
// recommended to simply pass a nil pointer cast to the appropriate type as
|
||||
// previously shown.
|
||||
//
|
||||
// The provided descriptions map must contain all of the keys or an error will
|
||||
// be returned which includes the missing key, or the final missing key when
|
||||
// there is more than one key missing. The generated help in the case of such
|
||||
// an error will use the key in place of the description.
|
||||
//
|
||||
// The following outlines the required keys:
|
||||
// "<method>--synopsis" Synopsis for the command
|
||||
// "<method>-<lowerfieldname>" Description for each command argument
|
||||
// "<typename>-<lowerfieldname>" Description for each object field
|
||||
// "<method>--condition<#>" Description for each result condition
|
||||
// "<method>--result<#>" Description for each primitive result num
|
||||
//
|
||||
// Notice that the "special" keys synopsis, condition<#>, and result<#> are
|
||||
// preceded by a double dash to ensure they don't conflict with field names.
|
||||
//
|
||||
// The condition keys are only required when there is more than on result type,
|
||||
// and the result key for a given result type is only required if it's not an
|
||||
// object.
|
||||
//
|
||||
// For example, consider the 'help' command itself. There are two possible
|
||||
// returns depending on the provided parameters. So, the help would be
|
||||
// generated by calling the function as follows:
|
||||
// GenerateHelp("help", descs, (*string)(nil), (*string)(nil)).
|
||||
//
|
||||
// The following keys would then be required in the provided descriptions map:
|
||||
//
|
||||
// "help--synopsis": "Returns a list of all commands or help for ...."
|
||||
// "help-command": "The command to retrieve help for",
|
||||
// "help--condition0": "no command provided"
|
||||
// "help--condition1": "command specified"
|
||||
// "help--result0": "List of commands"
|
||||
// "help--result1": "Help for specified command"
|
||||
func GenerateHelp(method string, descs map[string]string, resultTypes ...interface{}) (string, error) {
|
||||
// Look up details about the provided method and error out if not
|
||||
// registered.
|
||||
registerLock.RLock()
|
||||
rtp, ok := methodToConcreteType[method]
|
||||
info := methodToInfo[method]
|
||||
registerLock.RUnlock()
|
||||
if !ok {
|
||||
str := fmt.Sprintf("%q is not registered", method)
|
||||
return "", makeError(ErrUnregisteredMethod, str)
|
||||
}
|
||||
|
||||
// Validate each result type is a pointer to a supported type (or nil).
|
||||
for i, resultType := range resultTypes {
|
||||
if resultType == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rtp := reflect.TypeOf(resultType)
|
||||
if rtp.Kind() != reflect.Ptr {
|
||||
str := fmt.Sprintf("result #%d (%v) is not a pointer",
|
||||
i, rtp.Kind())
|
||||
return "", makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
elemKind := rtp.Elem().Kind()
|
||||
if !isValidResultType(elemKind) {
|
||||
str := fmt.Sprintf("result #%d (%v) is not an allowed "+
|
||||
"type", i, elemKind)
|
||||
return "", makeError(ErrInvalidType, str)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a closure for the description lookup function which falls back
|
||||
// to the base help descriptions map for unrecognized keys and tracks
|
||||
// and missing keys.
|
||||
var missingKey string
|
||||
xT := func(key string) string {
|
||||
if desc, ok := descs[key]; ok {
|
||||
return desc
|
||||
}
|
||||
if desc, ok := baseHelpDescs[key]; ok {
|
||||
return desc
|
||||
}
|
||||
|
||||
missingKey = key
|
||||
return key
|
||||
}
|
||||
|
||||
// Generate and return the help for the method.
|
||||
help := methodHelp(xT, rtp, info.defaults, method, resultTypes)
|
||||
if missingKey != "" {
|
||||
return help, makeError(ErrMissingDescription, missingKey)
|
||||
}
|
||||
return help, nil
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
// Bool is a helper routine that allocates a new bool value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Bool(v bool) *bool {
|
||||
p := new(bool)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Int is a helper routine that allocates a new int value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Int(v int) *int {
|
||||
p := new(int)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Uint is a helper routine that allocates a new uint value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Uint(v uint) *uint {
|
||||
p := new(uint)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Int32 is a helper routine that allocates a new int32 value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Int32(v int32) *int32 {
|
||||
p := new(int32)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Uint32 is a helper routine that allocates a new uint32 value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Uint32(v uint32) *uint32 {
|
||||
p := new(uint32)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Int64 is a helper routine that allocates a new int64 value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Int64(v int64) *int64 {
|
||||
p := new(int64)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Uint64 is a helper routine that allocates a new uint64 value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Uint64(v uint64) *uint64 {
|
||||
p := new(uint64)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// Float64 is a helper routine that allocates a new float64 value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func Float64(v float64) *float64 {
|
||||
p := new(float64)
|
||||
*p = v
|
||||
return p
|
||||
}
|
||||
|
||||
// String is a helper routine that allocates a new string value to store v and
|
||||
// returns a pointer to it. This is useful when assigning optional parameters.
|
||||
func String(v string) *string {
|
||||
p := new(string)
|
||||
*p = v
|
||||
return p
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RPCErrorCode represents an error code to be used as a part of an RPCError
|
||||
// which is in turn used in a JSON-RPC Response object.
|
||||
//
|
||||
// A specific type is used to help ensure the wrong errors aren't used.
|
||||
type RPCErrorCode int
|
||||
|
||||
// RPCError represents an error that is used as a part of a JSON-RPC Response
|
||||
// object.
|
||||
type RPCError struct {
|
||||
Code RPCErrorCode `json:"code,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// Guarantee RPCError satisifies the builtin error interface.
|
||||
var _, _ error = RPCError{}, (*RPCError)(nil)
|
||||
|
||||
// Error returns a string describing the RPC error. This satisifies the
|
||||
// builtin error interface.
|
||||
func (e RPCError) Error() string {
|
||||
return fmt.Sprintf("%d: %s", e.Code, e.Message)
|
||||
}
|
||||
|
||||
// NewRPCError constructs and returns a new JSON-RPC error that is suitable
|
||||
// for use in a JSON-RPC Response object.
|
||||
func NewRPCError(code RPCErrorCode, message string) *RPCError {
|
||||
return &RPCError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidIDType checks that the ID field (which can go in any of the JSON-RPC
|
||||
// requests, responses, or notifications) is valid. JSON-RPC 1.0 allows any
|
||||
// valid JSON type. JSON-RPC 2.0 (which bitcoind follows for some parts) only
|
||||
// allows string, number, or null, so this function restricts the allowed types
|
||||
// to that list. This function is only provided in case the caller is manually
|
||||
// marshalling for some reason. The functions which accept an ID in this
|
||||
// package already call this function to ensure the provided id is valid.
|
||||
func IsValidIDType(id interface{}) bool {
|
||||
switch id.(type) {
|
||||
case int, int8, int16, int32, int64,
|
||||
uint, uint8, uint16, uint32, uint64,
|
||||
float32, float64,
|
||||
string,
|
||||
nil:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Request is a type for raw JSON-RPC 1.0 requests. The Method field identifies
|
||||
// the specific command type which in turns leads to different parameters.
|
||||
// Callers typically will not use this directly since this package provides a
|
||||
// statically typed command infrastructure which handles creation of these
|
||||
// requests, however this struct it being exported in case the caller wants to
|
||||
// construct raw requests for some reason.
|
||||
type Request struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params []json.RawMessage `json:"params"`
|
||||
ID interface{} `json:"id"`
|
||||
}
|
||||
|
||||
// NewRequest returns a new JSON-RPC 1.0 request object given the provided id,
|
||||
// method, and parameters. The parameters are marshalled into a json.RawMessage
|
||||
// for the Params field of the returned request object. This function is only
|
||||
// provided in case the caller wants to construct raw requests for some reason.
|
||||
//
|
||||
// Typically callers will instead want to create a registered concrete command
|
||||
// type with the NewCmd or New<Foo>Cmd functions and call the MarshalCmd
|
||||
// function with that command to generate the marshalled JSON-RPC request.
|
||||
func NewRequest(id interface{}, method string, params []interface{}) (*Request, error) {
|
||||
if !IsValidIDType(id) {
|
||||
str := fmt.Sprintf("the id of type '%T' is invalid", id)
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
rawParams := make([]json.RawMessage, 0, len(params))
|
||||
for _, param := range params {
|
||||
marshalledParam, err := json.Marshal(param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawMessage := json.RawMessage(marshalledParam)
|
||||
rawParams = append(rawParams, rawMessage)
|
||||
}
|
||||
|
||||
return &Request{
|
||||
Jsonrpc: "1.0",
|
||||
ID: id,
|
||||
Method: method,
|
||||
Params: rawParams,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Response is the general form of a JSON-RPC response. The type of the Result
|
||||
// field varies from one command to the next, so it is implemented as an
|
||||
// interface. The ID field has to be a pointer for Go to put a null in it when
|
||||
// empty.
|
||||
type Response struct {
|
||||
Result json.RawMessage `json:"result"`
|
||||
Error *RPCError `json:"error"`
|
||||
ID *interface{} `json:"id"`
|
||||
}
|
||||
|
||||
// NewResponse returns a new JSON-RPC response object given the provided id,
|
||||
// marshalled result, and RPC error. This function is only provided in case the
|
||||
// caller wants to construct raw responses for some reason.
|
||||
//
|
||||
// Typically callers will instead want to create the fully marshalled JSON-RPC
|
||||
// response to send over the wire with the MarshalResponse function.
|
||||
func NewResponse(id interface{}, marshalledResult []byte, rpcErr *RPCError) (*Response, error) {
|
||||
if !IsValidIDType(id) {
|
||||
str := fmt.Sprintf("the id of type '%T' is invalid", id)
|
||||
return nil, makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
pid := &id
|
||||
return &Response{
|
||||
Result: marshalledResult,
|
||||
Error: rpcErr,
|
||||
ID: pid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalResponse marshals the passed id, result, and RPCError to a JSON-RPC
|
||||
// response byte slice that is suitable for transmission to a JSON-RPC client.
|
||||
func MarshalResponse(id interface{}, result interface{}, rpcErr *RPCError) ([]byte, error) {
|
||||
marshalledResult, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response, err := NewResponse(id, marshalledResult, rpcErr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(&response)
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
// Standard JSON-RPC 2.0 errors.
|
||||
var (
|
||||
ErrRPCInvalidRequest = &RPCError{
|
||||
Code: -32600,
|
||||
Message: "Invalid request",
|
||||
}
|
||||
ErrRPCMethodNotFound = &RPCError{
|
||||
Code: -32601,
|
||||
Message: "Method not found",
|
||||
}
|
||||
ErrRPCInvalidParams = &RPCError{
|
||||
Code: -32602,
|
||||
Message: "Invalid parameters",
|
||||
}
|
||||
ErrRPCInternal = &RPCError{
|
||||
Code: -32603,
|
||||
Message: "Internal error",
|
||||
}
|
||||
ErrRPCParse = &RPCError{
|
||||
Code: -32700,
|
||||
Message: "Parse error",
|
||||
}
|
||||
)
|
||||
|
||||
// General application defined JSON errors.
|
||||
const (
|
||||
ErrRPCMisc RPCErrorCode = -1
|
||||
ErrRPCForbiddenBySafeMode RPCErrorCode = -2
|
||||
ErrRPCType RPCErrorCode = -3
|
||||
ErrRPCInvalidAddressOrKey RPCErrorCode = -5
|
||||
ErrRPCOutOfMemory RPCErrorCode = -7
|
||||
ErrRPCInvalidParameter RPCErrorCode = -8
|
||||
ErrRPCDatabase RPCErrorCode = -20
|
||||
ErrRPCDeserialization RPCErrorCode = -22
|
||||
ErrRPCVerify RPCErrorCode = -25
|
||||
)
|
||||
|
||||
// Peer-to-peer client errors.
|
||||
const (
|
||||
ErrRPCClientNotConnected RPCErrorCode = -9
|
||||
ErrRPCClientInInitialDownload RPCErrorCode = -10
|
||||
ErrRPCClientNodeNotAdded RPCErrorCode = -24
|
||||
)
|
||||
|
||||
// Wallet JSON errors
|
||||
const (
|
||||
ErrRPCWallet RPCErrorCode = -4
|
||||
ErrRPCWalletInsufficientFunds RPCErrorCode = -6
|
||||
ErrRPCWalletInvalidAccountName RPCErrorCode = -11
|
||||
ErrRPCWalletKeypoolRanOut RPCErrorCode = -12
|
||||
ErrRPCWalletUnlockNeeded RPCErrorCode = -13
|
||||
ErrRPCWalletPassphraseIncorrect RPCErrorCode = -14
|
||||
ErrRPCWalletWrongEncState RPCErrorCode = -15
|
||||
ErrRPCWalletEncryptionFailed RPCErrorCode = -16
|
||||
ErrRPCWalletAlreadyUnlocked RPCErrorCode = -17
|
||||
)
|
||||
|
||||
// Specific Errors related to commands. These are the ones a user of the RPC
|
||||
// server are most likely to see. Generally, the codes should match one of the
|
||||
// more general errors above.
|
||||
const (
|
||||
ErrRPCBlockNotFound RPCErrorCode = -5
|
||||
ErrRPCBlockCount RPCErrorCode = -5
|
||||
ErrRPCBestBlockHash RPCErrorCode = -5
|
||||
ErrRPCDifficulty RPCErrorCode = -5
|
||||
ErrRPCOutOfRange RPCErrorCode = -1
|
||||
ErrRPCNoTxInfo RPCErrorCode = -5
|
||||
ErrRPCNoCFIndex RPCErrorCode = -5
|
||||
ErrRPCNoNewestBlockInfo RPCErrorCode = -5
|
||||
ErrRPCInvalidTxVout RPCErrorCode = -5
|
||||
ErrRPCRawTxString RPCErrorCode = -32602
|
||||
ErrRPCDecodeHexString RPCErrorCode = -22
|
||||
ErrRPCTxError RPCErrorCode = -25
|
||||
ErrRPCTxRejected RPCErrorCode = -26
|
||||
ErrRPCTxAlreadyInChain RPCErrorCode = -27
|
||||
)
|
||||
|
||||
// Errors that are specific to btcd.
|
||||
const (
|
||||
ErrRPCNoWallet RPCErrorCode = -1
|
||||
ErrRPCUnimplemented RPCErrorCode = -1
|
||||
)
|
|
@ -0,0 +1,292 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// UsageFlag define flags that specify additional properties about the
|
||||
// circumstances under which a command can be used.
|
||||
type UsageFlag uint32
|
||||
|
||||
const (
|
||||
// UFWalletOnly indicates that the command can only be used with an RPC
|
||||
// server that supports wallet commands.
|
||||
UFWalletOnly UsageFlag = 1 << iota
|
||||
|
||||
// UFWebsocketOnly indicates that the command can only be used when
|
||||
// communicating with an RPC server over websockets. This typically
|
||||
// applies to notifications and notification registration functions
|
||||
// since neiher makes since when using a single-shot HTTP-POST request.
|
||||
UFWebsocketOnly
|
||||
|
||||
// UFNotification indicates that the command is actually a notification.
|
||||
// This means when it is marshalled, the ID must be nil.
|
||||
UFNotification
|
||||
|
||||
// highestUsageFlagBit is the maximum usage flag bit and is used in the
|
||||
// stringer and tests to ensure all of the above constants have been
|
||||
// tested.
|
||||
highestUsageFlagBit
|
||||
)
|
||||
|
||||
// Map of UsageFlag values back to their constant names for pretty printing.
|
||||
var usageFlagStrings = map[UsageFlag]string{
|
||||
UFWalletOnly: "UFWalletOnly",
|
||||
UFWebsocketOnly: "UFWebsocketOnly",
|
||||
UFNotification: "UFNotification",
|
||||
}
|
||||
|
||||
// String returns the UsageFlag in human-readable form.
|
||||
func (fl UsageFlag) String() string {
|
||||
// No flags are set.
|
||||
if fl == 0 {
|
||||
return "0x0"
|
||||
}
|
||||
|
||||
// Add individual bit flags.
|
||||
s := ""
|
||||
for flag := UFWalletOnly; flag < highestUsageFlagBit; flag <<= 1 {
|
||||
if fl&flag == flag {
|
||||
s += usageFlagStrings[flag] + "|"
|
||||
fl -= flag
|
||||
}
|
||||
}
|
||||
|
||||
// Add remaining value as raw hex.
|
||||
s = strings.TrimRight(s, "|")
|
||||
if fl != 0 {
|
||||
s += "|0x" + strconv.FormatUint(uint64(fl), 16)
|
||||
}
|
||||
s = strings.TrimLeft(s, "|")
|
||||
return s
|
||||
}
|
||||
|
||||
// methodInfo keeps track of information about each registered method such as
|
||||
// the parameter information.
|
||||
type methodInfo struct {
|
||||
maxParams int
|
||||
numReqParams int
|
||||
numOptParams int
|
||||
defaults map[int]reflect.Value
|
||||
flags UsageFlag
|
||||
usage string
|
||||
}
|
||||
|
||||
var (
|
||||
// These fields are used to map the registered types to method names.
|
||||
registerLock sync.RWMutex
|
||||
methodToConcreteType = make(map[string]reflect.Type)
|
||||
methodToInfo = make(map[string]methodInfo)
|
||||
concreteTypeToMethod = make(map[reflect.Type]string)
|
||||
)
|
||||
|
||||
// baseKindString returns the base kind for a given reflect.Type after
|
||||
// indirecting through all pointers.
|
||||
func baseKindString(rt reflect.Type) string {
|
||||
numIndirects := 0
|
||||
for rt.Kind() == reflect.Ptr {
|
||||
numIndirects++
|
||||
rt = rt.Elem()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s", strings.Repeat("*", numIndirects), rt.Kind())
|
||||
}
|
||||
|
||||
// isAcceptableKind returns whether or not the passed field type is a supported
|
||||
// type. It is called after the first pointer indirection, so further pointers
|
||||
// are not supported.
|
||||
func isAcceptableKind(kind reflect.Kind) bool {
|
||||
switch kind {
|
||||
case reflect.Chan:
|
||||
fallthrough
|
||||
case reflect.Complex64:
|
||||
fallthrough
|
||||
case reflect.Complex128:
|
||||
fallthrough
|
||||
case reflect.Func:
|
||||
fallthrough
|
||||
case reflect.Ptr:
|
||||
fallthrough
|
||||
case reflect.Interface:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// RegisterCmd registers a new command that will automatically marshal to and
|
||||
// from JSON-RPC with full type checking and positional parameter support. It
|
||||
// also accepts usage flags which identify the circumstances under which the
|
||||
// command can be used.
|
||||
//
|
||||
// This package automatically registers all of the exported commands by default
|
||||
// using this function, however it is also exported so callers can easily
|
||||
// register custom types.
|
||||
//
|
||||
// The type format is very strict since it needs to be able to automatically
|
||||
// marshal to and from JSON-RPC 1.0. The following enumerates the requirements:
|
||||
//
|
||||
// - The provided command must be a single pointer to a struct
|
||||
// - All fields must be exported
|
||||
// - The order of the positional parameters in the marshalled JSON will be in
|
||||
// the same order as declared in the struct definition
|
||||
// - Struct embedding is not supported
|
||||
// - Struct fields may NOT be channels, functions, complex, or interface
|
||||
// - A field in the provided struct with a pointer is treated as optional
|
||||
// - Multiple indirections (i.e **int) are not supported
|
||||
// - Once the first optional field (pointer) is encountered, the remaining
|
||||
// fields must also be optional fields (pointers) as required by positional
|
||||
// params
|
||||
// - A field that has a 'jsonrpcdefault' struct tag must be an optional field
|
||||
// (pointer)
|
||||
//
|
||||
// NOTE: This function only needs to be able to examine the structure of the
|
||||
// passed struct, so it does not need to be an actual instance. Therefore, it
|
||||
// is recommended to simply pass a nil pointer cast to the appropriate type.
|
||||
// For example, (*FooCmd)(nil).
|
||||
func RegisterCmd(method string, cmd interface{}, flags UsageFlag) error {
|
||||
registerLock.Lock()
|
||||
defer registerLock.Unlock()
|
||||
|
||||
if _, ok := methodToConcreteType[method]; ok {
|
||||
str := fmt.Sprintf("method %q is already registered", method)
|
||||
return makeError(ErrDuplicateMethod, str)
|
||||
}
|
||||
|
||||
// Ensure that no unrecognized flag bits were specified.
|
||||
if ^(highestUsageFlagBit-1)&flags != 0 {
|
||||
str := fmt.Sprintf("invalid usage flags specified for method "+
|
||||
"%s: %v", method, flags)
|
||||
return makeError(ErrInvalidUsageFlags, str)
|
||||
}
|
||||
|
||||
rtp := reflect.TypeOf(cmd)
|
||||
if rtp.Kind() != reflect.Ptr {
|
||||
str := fmt.Sprintf("type must be *struct not '%s (%s)'", rtp,
|
||||
rtp.Kind())
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
rt := rtp.Elem()
|
||||
if rt.Kind() != reflect.Struct {
|
||||
str := fmt.Sprintf("type must be *struct not '%s (*%s)'",
|
||||
rtp, rt.Kind())
|
||||
return makeError(ErrInvalidType, str)
|
||||
}
|
||||
|
||||
// Enumerate the struct fields to validate them and gather parameter
|
||||
// information.
|
||||
numFields := rt.NumField()
|
||||
numOptFields := 0
|
||||
defaults := make(map[int]reflect.Value)
|
||||
for i := 0; i < numFields; i++ {
|
||||
rtf := rt.Field(i)
|
||||
if rtf.Anonymous {
|
||||
str := fmt.Sprintf("embedded fields are not supported "+
|
||||
"(field name: %q)", rtf.Name)
|
||||
return makeError(ErrEmbeddedType, str)
|
||||
}
|
||||
if rtf.PkgPath != "" {
|
||||
str := fmt.Sprintf("unexported fields are not supported "+
|
||||
"(field name: %q)", rtf.Name)
|
||||
return makeError(ErrUnexportedField, str)
|
||||
}
|
||||
|
||||
// Disallow types that can't be JSON encoded. Also, determine
|
||||
// if the field is optional based on it being a pointer.
|
||||
var isOptional bool
|
||||
switch kind := rtf.Type.Kind(); kind {
|
||||
case reflect.Ptr:
|
||||
isOptional = true
|
||||
kind = rtf.Type.Elem().Kind()
|
||||
fallthrough
|
||||
default:
|
||||
if !isAcceptableKind(kind) {
|
||||
str := fmt.Sprintf("unsupported field type "+
|
||||
"'%s (%s)' (field name %q)", rtf.Type,
|
||||
baseKindString(rtf.Type), rtf.Name)
|
||||
return makeError(ErrUnsupportedFieldType, str)
|
||||
}
|
||||
}
|
||||
|
||||
// Count the optional fields and ensure all fields after the
|
||||
// first optional field are also optional.
|
||||
if isOptional {
|
||||
numOptFields++
|
||||
} else {
|
||||
if numOptFields > 0 {
|
||||
str := fmt.Sprintf("all fields after the first "+
|
||||
"optional field must also be optional "+
|
||||
"(field name %q)", rtf.Name)
|
||||
return makeError(ErrNonOptionalField, str)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the default value can be unsmarshalled into the type
|
||||
// and that defaults are only specified for optional fields.
|
||||
if tag := rtf.Tag.Get("jsonrpcdefault"); tag != "" {
|
||||
if !isOptional {
|
||||
str := fmt.Sprintf("required fields must not "+
|
||||
"have a default specified (field name "+
|
||||
"%q)", rtf.Name)
|
||||
return makeError(ErrNonOptionalDefault, str)
|
||||
}
|
||||
|
||||
rvf := reflect.New(rtf.Type.Elem())
|
||||
err := json.Unmarshal([]byte(tag), rvf.Interface())
|
||||
if err != nil {
|
||||
str := fmt.Sprintf("default value of %q is "+
|
||||
"the wrong type (field name %q)", tag,
|
||||
rtf.Name)
|
||||
return makeError(ErrMismatchedDefault, str)
|
||||
}
|
||||
defaults[i] = rvf
|
||||
}
|
||||
}
|
||||
|
||||
// Update the registration maps.
|
||||
methodToConcreteType[method] = rtp
|
||||
methodToInfo[method] = methodInfo{
|
||||
maxParams: numFields,
|
||||
numReqParams: numFields - numOptFields,
|
||||
numOptParams: numOptFields,
|
||||
defaults: defaults,
|
||||
flags: flags,
|
||||
}
|
||||
concreteTypeToMethod[rtp] = method
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustRegisterCmd performs the same function as RegisterCmd except it panics
|
||||
// if there is an error. This should only be called from package init
|
||||
// functions.
|
||||
func MustRegisterCmd(method string, cmd interface{}, flags UsageFlag) {
|
||||
if err := RegisterCmd(method, cmd, flags); err != nil {
|
||||
panic(fmt.Sprintf("failed to register type %q: %v\n", method,
|
||||
err))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisteredCmdMethods returns a sorted list of methods for all registered
|
||||
// commands.
|
||||
func RegisteredCmdMethods() []string {
|
||||
registerLock.Lock()
|
||||
defer registerLock.Unlock()
|
||||
|
||||
methods := make([]string, 0, len(methodToInfo))
|
||||
for k := range methodToInfo {
|
||||
methods = append(methods, k)
|
||||
}
|
||||
|
||||
sort.Sort(sort.StringSlice(methods))
|
||||
return methods
|
||||
}
|
|
@ -0,0 +1,699 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a wallet server.
|
||||
|
||||
package btcjson
|
||||
|
||||
// AddMultisigAddressCmd defines the addmutisigaddress JSON-RPC command.
|
||||
type AddMultisigAddressCmd struct {
|
||||
NRequired int
|
||||
Keys []string
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewAddMultisigAddressCmd returns a new instance which can be used to issue a
|
||||
// addmultisigaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewAddMultisigAddressCmd(nRequired int, keys []string, account *string) *AddMultisigAddressCmd {
|
||||
return &AddMultisigAddressCmd{
|
||||
NRequired: nRequired,
|
||||
Keys: keys,
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// AddWitnessAddressCmd defines the addwitnessaddress JSON-RPC command.
|
||||
type AddWitnessAddressCmd struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
// NewAddWitnessAddressCmd returns a new instance which can be used to issue a
|
||||
// addwitnessaddress JSON-RPC command.
|
||||
func NewAddWitnessAddressCmd(address string) *AddWitnessAddressCmd {
|
||||
return &AddWitnessAddressCmd{
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateMultisigCmd defines the createmultisig JSON-RPC command.
|
||||
type CreateMultisigCmd struct {
|
||||
NRequired int
|
||||
Keys []string
|
||||
}
|
||||
|
||||
// NewCreateMultisigCmd returns a new instance which can be used to issue a
|
||||
// createmultisig JSON-RPC command.
|
||||
func NewCreateMultisigCmd(nRequired int, keys []string) *CreateMultisigCmd {
|
||||
return &CreateMultisigCmd{
|
||||
NRequired: nRequired,
|
||||
Keys: keys,
|
||||
}
|
||||
}
|
||||
|
||||
// DumpPrivKeyCmd defines the dumpprivkey JSON-RPC command.
|
||||
type DumpPrivKeyCmd struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
// NewDumpPrivKeyCmd returns a new instance which can be used to issue a
|
||||
// dumpprivkey JSON-RPC command.
|
||||
func NewDumpPrivKeyCmd(address string) *DumpPrivKeyCmd {
|
||||
return &DumpPrivKeyCmd{
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptWalletCmd defines the encryptwallet JSON-RPC command.
|
||||
type EncryptWalletCmd struct {
|
||||
Passphrase string
|
||||
}
|
||||
|
||||
// NewEncryptWalletCmd returns a new instance which can be used to issue a
|
||||
// encryptwallet JSON-RPC command.
|
||||
func NewEncryptWalletCmd(passphrase string) *EncryptWalletCmd {
|
||||
return &EncryptWalletCmd{
|
||||
Passphrase: passphrase,
|
||||
}
|
||||
}
|
||||
|
||||
// EstimateFeeCmd defines the estimatefee JSON-RPC command.
|
||||
type EstimateFeeCmd struct {
|
||||
NumBlocks int64
|
||||
}
|
||||
|
||||
// NewEstimateFeeCmd returns a new instance which can be used to issue a
|
||||
// estimatefee JSON-RPC command.
|
||||
func NewEstimateFeeCmd(numBlocks int64) *EstimateFeeCmd {
|
||||
return &EstimateFeeCmd{
|
||||
NumBlocks: numBlocks,
|
||||
}
|
||||
}
|
||||
|
||||
// EstimatePriorityCmd defines the estimatepriority JSON-RPC command.
|
||||
type EstimatePriorityCmd struct {
|
||||
NumBlocks int64
|
||||
}
|
||||
|
||||
// NewEstimatePriorityCmd returns a new instance which can be used to issue a
|
||||
// estimatepriority JSON-RPC command.
|
||||
func NewEstimatePriorityCmd(numBlocks int64) *EstimatePriorityCmd {
|
||||
return &EstimatePriorityCmd{
|
||||
NumBlocks: numBlocks,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountCmd defines the getaccount JSON-RPC command.
|
||||
type GetAccountCmd struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
// NewGetAccountCmd returns a new instance which can be used to issue a
|
||||
// getaccount JSON-RPC command.
|
||||
func NewGetAccountCmd(address string) *GetAccountCmd {
|
||||
return &GetAccountCmd{
|
||||
Address: address,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAccountAddressCmd defines the getaccountaddress JSON-RPC command.
|
||||
type GetAccountAddressCmd struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
// NewGetAccountAddressCmd returns a new instance which can be used to issue a
|
||||
// getaccountaddress JSON-RPC command.
|
||||
func NewGetAccountAddressCmd(account string) *GetAccountAddressCmd {
|
||||
return &GetAccountAddressCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAddressesByAccountCmd defines the getaddressesbyaccount JSON-RPC command.
|
||||
type GetAddressesByAccountCmd struct {
|
||||
Account string
|
||||
}
|
||||
|
||||
// NewGetAddressesByAccountCmd returns a new instance which can be used to issue
|
||||
// a getaddressesbyaccount JSON-RPC command.
|
||||
func NewGetAddressesByAccountCmd(account string) *GetAddressesByAccountCmd {
|
||||
return &GetAddressesByAccountCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// GetBalanceCmd defines the getbalance JSON-RPC command.
|
||||
type GetBalanceCmd struct {
|
||||
Account *string
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
}
|
||||
|
||||
// NewGetBalanceCmd returns a new instance which can be used to issue a
|
||||
// getbalance JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetBalanceCmd(account *string, minConf *int) *GetBalanceCmd {
|
||||
return &GetBalanceCmd{
|
||||
Account: account,
|
||||
MinConf: minConf,
|
||||
}
|
||||
}
|
||||
|
||||
// GetNewAddressCmd defines the getnewaddress JSON-RPC command.
|
||||
type GetNewAddressCmd struct {
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewGetNewAddressCmd returns a new instance which can be used to issue a
|
||||
// getnewaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetNewAddressCmd(account *string) *GetNewAddressCmd {
|
||||
return &GetNewAddressCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRawChangeAddressCmd defines the getrawchangeaddress JSON-RPC command.
|
||||
type GetRawChangeAddressCmd struct {
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewGetRawChangeAddressCmd returns a new instance which can be used to issue a
|
||||
// getrawchangeaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetRawChangeAddressCmd(account *string) *GetRawChangeAddressCmd {
|
||||
return &GetRawChangeAddressCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// GetReceivedByAccountCmd defines the getreceivedbyaccount JSON-RPC command.
|
||||
type GetReceivedByAccountCmd struct {
|
||||
Account string
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
}
|
||||
|
||||
// NewGetReceivedByAccountCmd returns a new instance which can be used to issue
|
||||
// a getreceivedbyaccount JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetReceivedByAccountCmd(account string, minConf *int) *GetReceivedByAccountCmd {
|
||||
return &GetReceivedByAccountCmd{
|
||||
Account: account,
|
||||
MinConf: minConf,
|
||||
}
|
||||
}
|
||||
|
||||
// GetReceivedByAddressCmd defines the getreceivedbyaddress JSON-RPC command.
|
||||
type GetReceivedByAddressCmd struct {
|
||||
Address string
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
}
|
||||
|
||||
// NewGetReceivedByAddressCmd returns a new instance which can be used to issue
|
||||
// a getreceivedbyaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetReceivedByAddressCmd(address string, minConf *int) *GetReceivedByAddressCmd {
|
||||
return &GetReceivedByAddressCmd{
|
||||
Address: address,
|
||||
MinConf: minConf,
|
||||
}
|
||||
}
|
||||
|
||||
// GetTransactionCmd defines the gettransaction JSON-RPC command.
|
||||
type GetTransactionCmd struct {
|
||||
Txid string
|
||||
IncludeWatchOnly *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewGetTransactionCmd returns a new instance which can be used to issue a
|
||||
// gettransaction JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetTransactionCmd(txHash string, includeWatchOnly *bool) *GetTransactionCmd {
|
||||
return &GetTransactionCmd{
|
||||
Txid: txHash,
|
||||
IncludeWatchOnly: includeWatchOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// GetWalletInfoCmd defines the getwalletinfo JSON-RPC command.
|
||||
type GetWalletInfoCmd struct{}
|
||||
|
||||
// NewGetWalletInfoCmd returns a new instance which can be used to issue a
|
||||
// getwalletinfo JSON-RPC command.
|
||||
func NewGetWalletInfoCmd() *GetWalletInfoCmd {
|
||||
return &GetWalletInfoCmd{}
|
||||
}
|
||||
|
||||
// ImportPrivKeyCmd defines the importprivkey JSON-RPC command.
|
||||
type ImportPrivKeyCmd struct {
|
||||
PrivKey string
|
||||
Label *string
|
||||
Rescan *bool `jsonrpcdefault:"true"`
|
||||
}
|
||||
|
||||
// NewImportPrivKeyCmd returns a new instance which can be used to issue a
|
||||
// importprivkey JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewImportPrivKeyCmd(privKey string, label *string, rescan *bool) *ImportPrivKeyCmd {
|
||||
return &ImportPrivKeyCmd{
|
||||
PrivKey: privKey,
|
||||
Label: label,
|
||||
Rescan: rescan,
|
||||
}
|
||||
}
|
||||
|
||||
// KeyPoolRefillCmd defines the keypoolrefill JSON-RPC command.
|
||||
type KeyPoolRefillCmd struct {
|
||||
NewSize *uint `jsonrpcdefault:"100"`
|
||||
}
|
||||
|
||||
// NewKeyPoolRefillCmd returns a new instance which can be used to issue a
|
||||
// keypoolrefill JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewKeyPoolRefillCmd(newSize *uint) *KeyPoolRefillCmd {
|
||||
return &KeyPoolRefillCmd{
|
||||
NewSize: newSize,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAccountsCmd defines the listaccounts JSON-RPC command.
|
||||
type ListAccountsCmd struct {
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
}
|
||||
|
||||
// NewListAccountsCmd returns a new instance which can be used to issue a
|
||||
// listaccounts JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListAccountsCmd(minConf *int) *ListAccountsCmd {
|
||||
return &ListAccountsCmd{
|
||||
MinConf: minConf,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAddressGroupingsCmd defines the listaddressgroupings JSON-RPC command.
|
||||
type ListAddressGroupingsCmd struct{}
|
||||
|
||||
// NewListAddressGroupingsCmd returns a new instance which can be used to issue
|
||||
// a listaddressgroupoings JSON-RPC command.
|
||||
func NewListAddressGroupingsCmd() *ListAddressGroupingsCmd {
|
||||
return &ListAddressGroupingsCmd{}
|
||||
}
|
||||
|
||||
// ListLockUnspentCmd defines the listlockunspent JSON-RPC command.
|
||||
type ListLockUnspentCmd struct{}
|
||||
|
||||
// NewListLockUnspentCmd returns a new instance which can be used to issue a
|
||||
// listlockunspent JSON-RPC command.
|
||||
func NewListLockUnspentCmd() *ListLockUnspentCmd {
|
||||
return &ListLockUnspentCmd{}
|
||||
}
|
||||
|
||||
// ListReceivedByAccountCmd defines the listreceivedbyaccount JSON-RPC command.
|
||||
type ListReceivedByAccountCmd struct {
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
IncludeEmpty *bool `jsonrpcdefault:"false"`
|
||||
IncludeWatchOnly *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewListReceivedByAccountCmd returns a new instance which can be used to issue
|
||||
// a listreceivedbyaccount JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListReceivedByAccountCmd(minConf *int, includeEmpty, includeWatchOnly *bool) *ListReceivedByAccountCmd {
|
||||
return &ListReceivedByAccountCmd{
|
||||
MinConf: minConf,
|
||||
IncludeEmpty: includeEmpty,
|
||||
IncludeWatchOnly: includeWatchOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// ListReceivedByAddressCmd defines the listreceivedbyaddress JSON-RPC command.
|
||||
type ListReceivedByAddressCmd struct {
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
IncludeEmpty *bool `jsonrpcdefault:"false"`
|
||||
IncludeWatchOnly *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewListReceivedByAddressCmd returns a new instance which can be used to issue
|
||||
// a listreceivedbyaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListReceivedByAddressCmd(minConf *int, includeEmpty, includeWatchOnly *bool) *ListReceivedByAddressCmd {
|
||||
return &ListReceivedByAddressCmd{
|
||||
MinConf: minConf,
|
||||
IncludeEmpty: includeEmpty,
|
||||
IncludeWatchOnly: includeWatchOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// ListSinceBlockCmd defines the listsinceblock JSON-RPC command.
|
||||
type ListSinceBlockCmd struct {
|
||||
BlockHash *string
|
||||
TargetConfirmations *int `jsonrpcdefault:"1"`
|
||||
IncludeWatchOnly *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewListSinceBlockCmd returns a new instance which can be used to issue a
|
||||
// listsinceblock JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListSinceBlockCmd(blockHash *string, targetConfirms *int, includeWatchOnly *bool) *ListSinceBlockCmd {
|
||||
return &ListSinceBlockCmd{
|
||||
BlockHash: blockHash,
|
||||
TargetConfirmations: targetConfirms,
|
||||
IncludeWatchOnly: includeWatchOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// ListTransactionsCmd defines the listtransactions JSON-RPC command.
|
||||
type ListTransactionsCmd struct {
|
||||
Account *string
|
||||
Count *int `jsonrpcdefault:"10"`
|
||||
From *int `jsonrpcdefault:"0"`
|
||||
IncludeWatchOnly *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewListTransactionsCmd returns a new instance which can be used to issue a
|
||||
// listtransactions JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListTransactionsCmd(account *string, count, from *int, includeWatchOnly *bool) *ListTransactionsCmd {
|
||||
return &ListTransactionsCmd{
|
||||
Account: account,
|
||||
Count: count,
|
||||
From: from,
|
||||
IncludeWatchOnly: includeWatchOnly,
|
||||
}
|
||||
}
|
||||
|
||||
// ListUnspentCmd defines the listunspent JSON-RPC command.
|
||||
type ListUnspentCmd struct {
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
MaxConf *int `jsonrpcdefault:"9999999"`
|
||||
Addresses *[]string
|
||||
}
|
||||
|
||||
// NewListUnspentCmd returns a new instance which can be used to issue a
|
||||
// listunspent JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListUnspentCmd(minConf, maxConf *int, addresses *[]string) *ListUnspentCmd {
|
||||
return &ListUnspentCmd{
|
||||
MinConf: minConf,
|
||||
MaxConf: maxConf,
|
||||
Addresses: addresses,
|
||||
}
|
||||
}
|
||||
|
||||
// LockUnspentCmd defines the lockunspent JSON-RPC command.
|
||||
type LockUnspentCmd struct {
|
||||
Unlock bool
|
||||
Transactions []TransactionInput
|
||||
}
|
||||
|
||||
// NewLockUnspentCmd returns a new instance which can be used to issue a
|
||||
// lockunspent JSON-RPC command.
|
||||
func NewLockUnspentCmd(unlock bool, transactions []TransactionInput) *LockUnspentCmd {
|
||||
return &LockUnspentCmd{
|
||||
Unlock: unlock,
|
||||
Transactions: transactions,
|
||||
}
|
||||
}
|
||||
|
||||
// MoveCmd defines the move JSON-RPC command.
|
||||
type MoveCmd struct {
|
||||
FromAccount string
|
||||
ToAccount string
|
||||
Amount float64 // In BTC
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
Comment *string
|
||||
}
|
||||
|
||||
// NewMoveCmd returns a new instance which can be used to issue a move JSON-RPC
|
||||
// command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewMoveCmd(fromAccount, toAccount string, amount float64, minConf *int, comment *string) *MoveCmd {
|
||||
return &MoveCmd{
|
||||
FromAccount: fromAccount,
|
||||
ToAccount: toAccount,
|
||||
Amount: amount,
|
||||
MinConf: minConf,
|
||||
Comment: comment,
|
||||
}
|
||||
}
|
||||
|
||||
// SendFromCmd defines the sendfrom JSON-RPC command.
|
||||
type SendFromCmd struct {
|
||||
FromAccount string
|
||||
ToAddress string
|
||||
Amount float64 // In BTC
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
Comment *string
|
||||
CommentTo *string
|
||||
}
|
||||
|
||||
// NewSendFromCmd returns a new instance which can be used to issue a sendfrom
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSendFromCmd(fromAccount, toAddress string, amount float64, minConf *int, comment, commentTo *string) *SendFromCmd {
|
||||
return &SendFromCmd{
|
||||
FromAccount: fromAccount,
|
||||
ToAddress: toAddress,
|
||||
Amount: amount,
|
||||
MinConf: minConf,
|
||||
Comment: comment,
|
||||
CommentTo: commentTo,
|
||||
}
|
||||
}
|
||||
|
||||
// SendManyCmd defines the sendmany JSON-RPC command.
|
||||
type SendManyCmd struct {
|
||||
FromAccount string
|
||||
Amounts map[string]float64 `jsonrpcusage:"{\"address\":amount,...}"` // In BTC
|
||||
MinConf *int `jsonrpcdefault:"1"`
|
||||
Comment *string
|
||||
}
|
||||
|
||||
// NewSendManyCmd returns a new instance which can be used to issue a sendmany
|
||||
// JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSendManyCmd(fromAccount string, amounts map[string]float64, minConf *int, comment *string) *SendManyCmd {
|
||||
return &SendManyCmd{
|
||||
FromAccount: fromAccount,
|
||||
Amounts: amounts,
|
||||
MinConf: minConf,
|
||||
Comment: comment,
|
||||
}
|
||||
}
|
||||
|
||||
// SendToAddressCmd defines the sendtoaddress JSON-RPC command.
|
||||
type SendToAddressCmd struct {
|
||||
Address string
|
||||
Amount float64
|
||||
Comment *string
|
||||
CommentTo *string
|
||||
}
|
||||
|
||||
// NewSendToAddressCmd returns a new instance which can be used to issue a
|
||||
// sendtoaddress JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSendToAddressCmd(address string, amount float64, comment, commentTo *string) *SendToAddressCmd {
|
||||
return &SendToAddressCmd{
|
||||
Address: address,
|
||||
Amount: amount,
|
||||
Comment: comment,
|
||||
CommentTo: commentTo,
|
||||
}
|
||||
}
|
||||
|
||||
// SetAccountCmd defines the setaccount JSON-RPC command.
|
||||
type SetAccountCmd struct {
|
||||
Address string
|
||||
Account string
|
||||
}
|
||||
|
||||
// NewSetAccountCmd returns a new instance which can be used to issue a
|
||||
// setaccount JSON-RPC command.
|
||||
func NewSetAccountCmd(address, account string) *SetAccountCmd {
|
||||
return &SetAccountCmd{
|
||||
Address: address,
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// SetTxFeeCmd defines the settxfee JSON-RPC command.
|
||||
type SetTxFeeCmd struct {
|
||||
Amount float64 // In BTC
|
||||
}
|
||||
|
||||
// NewSetTxFeeCmd returns a new instance which can be used to issue a settxfee
|
||||
// JSON-RPC command.
|
||||
func NewSetTxFeeCmd(amount float64) *SetTxFeeCmd {
|
||||
return &SetTxFeeCmd{
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
// SignMessageCmd defines the signmessage JSON-RPC command.
|
||||
type SignMessageCmd struct {
|
||||
Address string
|
||||
Message string
|
||||
}
|
||||
|
||||
// NewSignMessageCmd returns a new instance which can be used to issue a
|
||||
// signmessage JSON-RPC command.
|
||||
func NewSignMessageCmd(address, message string) *SignMessageCmd {
|
||||
return &SignMessageCmd{
|
||||
Address: address,
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
|
||||
// RawTxInput models the data needed for raw transaction input that is used in
|
||||
// the SignRawTransactionCmd struct.
|
||||
type RawTxInput struct {
|
||||
Txid string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptPubKey string `json:"scriptPubKey"`
|
||||
RedeemScript string `json:"redeemScript"`
|
||||
}
|
||||
|
||||
// SignRawTransactionCmd defines the signrawtransaction JSON-RPC command.
|
||||
type SignRawTransactionCmd struct {
|
||||
RawTx string
|
||||
Inputs *[]RawTxInput
|
||||
PrivKeys *[]string
|
||||
Flags *string `jsonrpcdefault:"\"ALL\""`
|
||||
}
|
||||
|
||||
// NewSignRawTransactionCmd returns a new instance which can be used to issue a
|
||||
// signrawtransaction JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewSignRawTransactionCmd(hexEncodedTx string, inputs *[]RawTxInput, privKeys *[]string, flags *string) *SignRawTransactionCmd {
|
||||
return &SignRawTransactionCmd{
|
||||
RawTx: hexEncodedTx,
|
||||
Inputs: inputs,
|
||||
PrivKeys: privKeys,
|
||||
Flags: flags,
|
||||
}
|
||||
}
|
||||
|
||||
// WalletLockCmd defines the walletlock JSON-RPC command.
|
||||
type WalletLockCmd struct{}
|
||||
|
||||
// NewWalletLockCmd returns a new instance which can be used to issue a
|
||||
// walletlock JSON-RPC command.
|
||||
func NewWalletLockCmd() *WalletLockCmd {
|
||||
return &WalletLockCmd{}
|
||||
}
|
||||
|
||||
// WalletPassphraseCmd defines the walletpassphrase JSON-RPC command.
|
||||
type WalletPassphraseCmd struct {
|
||||
Passphrase string
|
||||
Timeout int64
|
||||
}
|
||||
|
||||
// NewWalletPassphraseCmd returns a new instance which can be used to issue a
|
||||
// walletpassphrase JSON-RPC command.
|
||||
func NewWalletPassphraseCmd(passphrase string, timeout int64) *WalletPassphraseCmd {
|
||||
return &WalletPassphraseCmd{
|
||||
Passphrase: passphrase,
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// WalletPassphraseChangeCmd defines the walletpassphrase JSON-RPC command.
|
||||
type WalletPassphraseChangeCmd struct {
|
||||
OldPassphrase string
|
||||
NewPassphrase string
|
||||
}
|
||||
|
||||
// NewWalletPassphraseChangeCmd returns a new instance which can be used to
|
||||
// issue a walletpassphrasechange JSON-RPC command.
|
||||
func NewWalletPassphraseChangeCmd(oldPassphrase, newPassphrase string) *WalletPassphraseChangeCmd {
|
||||
return &WalletPassphraseChangeCmd{
|
||||
OldPassphrase: oldPassphrase,
|
||||
NewPassphrase: newPassphrase,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable with a wallet server.
|
||||
flags := UFWalletOnly
|
||||
|
||||
MustRegisterCmd("addmultisigaddress", (*AddMultisigAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("addwitnessaddress", (*AddWitnessAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("createmultisig", (*CreateMultisigCmd)(nil), flags)
|
||||
MustRegisterCmd("dumpprivkey", (*DumpPrivKeyCmd)(nil), flags)
|
||||
MustRegisterCmd("encryptwallet", (*EncryptWalletCmd)(nil), flags)
|
||||
MustRegisterCmd("estimatefee", (*EstimateFeeCmd)(nil), flags)
|
||||
MustRegisterCmd("estimatepriority", (*EstimatePriorityCmd)(nil), flags)
|
||||
MustRegisterCmd("getaccount", (*GetAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("getaccountaddress", (*GetAccountAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("getaddressesbyaccount", (*GetAddressesByAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("getbalance", (*GetBalanceCmd)(nil), flags)
|
||||
MustRegisterCmd("getnewaddress", (*GetNewAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("getrawchangeaddress", (*GetRawChangeAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("getreceivedbyaccount", (*GetReceivedByAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("getreceivedbyaddress", (*GetReceivedByAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("gettransaction", (*GetTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("getwalletinfo", (*GetWalletInfoCmd)(nil), flags)
|
||||
MustRegisterCmd("importprivkey", (*ImportPrivKeyCmd)(nil), flags)
|
||||
MustRegisterCmd("keypoolrefill", (*KeyPoolRefillCmd)(nil), flags)
|
||||
MustRegisterCmd("listaccounts", (*ListAccountsCmd)(nil), flags)
|
||||
MustRegisterCmd("listaddressgroupings", (*ListAddressGroupingsCmd)(nil), flags)
|
||||
MustRegisterCmd("listlockunspent", (*ListLockUnspentCmd)(nil), flags)
|
||||
MustRegisterCmd("listreceivedbyaccount", (*ListReceivedByAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("listreceivedbyaddress", (*ListReceivedByAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("listsinceblock", (*ListSinceBlockCmd)(nil), flags)
|
||||
MustRegisterCmd("listtransactions", (*ListTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("listunspent", (*ListUnspentCmd)(nil), flags)
|
||||
MustRegisterCmd("lockunspent", (*LockUnspentCmd)(nil), flags)
|
||||
MustRegisterCmd("move", (*MoveCmd)(nil), flags)
|
||||
MustRegisterCmd("sendfrom", (*SendFromCmd)(nil), flags)
|
||||
MustRegisterCmd("sendmany", (*SendManyCmd)(nil), flags)
|
||||
MustRegisterCmd("sendtoaddress", (*SendToAddressCmd)(nil), flags)
|
||||
MustRegisterCmd("setaccount", (*SetAccountCmd)(nil), flags)
|
||||
MustRegisterCmd("settxfee", (*SetTxFeeCmd)(nil), flags)
|
||||
MustRegisterCmd("signmessage", (*SignMessageCmd)(nil), flags)
|
||||
MustRegisterCmd("signrawtransaction", (*SignRawTransactionCmd)(nil), flags)
|
||||
MustRegisterCmd("walletlock", (*WalletLockCmd)(nil), flags)
|
||||
MustRegisterCmd("walletpassphrase", (*WalletPassphraseCmd)(nil), flags)
|
||||
MustRegisterCmd("walletpassphrasechange", (*WalletPassphraseChangeCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
// GetTransactionDetailsResult models the details data from the gettransaction command.
|
||||
//
|
||||
// This models the "short" version of the ListTransactionsResult type, which
|
||||
// excludes fields common to the transaction. These common fields are instead
|
||||
// part of the GetTransactionResult.
|
||||
type GetTransactionDetailsResult struct {
|
||||
Account string `json:"account"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Amount float64 `json:"amount"`
|
||||
Category string `json:"category"`
|
||||
InvolvesWatchOnly bool `json:"involveswatchonly,omitempty"`
|
||||
Fee *float64 `json:"fee,omitempty"`
|
||||
Vout uint32 `json:"vout"`
|
||||
}
|
||||
|
||||
// GetTransactionResult models the data from the gettransaction command.
|
||||
type GetTransactionResult struct {
|
||||
Amount float64 `json:"amount"`
|
||||
Fee float64 `json:"fee,omitempty"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
BlockHash string `json:"blockhash"`
|
||||
BlockIndex int64 `json:"blockindex"`
|
||||
BlockTime int64 `json:"blocktime"`
|
||||
TxID string `json:"txid"`
|
||||
WalletConflicts []string `json:"walletconflicts"`
|
||||
Time int64 `json:"time"`
|
||||
TimeReceived int64 `json:"timereceived"`
|
||||
Details []GetTransactionDetailsResult `json:"details"`
|
||||
Hex string `json:"hex"`
|
||||
}
|
||||
|
||||
// InfoWalletResult models the data returned by the wallet server getinfo
|
||||
// command.
|
||||
type InfoWalletResult struct {
|
||||
Version int32 `json:"version"`
|
||||
ProtocolVersion int32 `json:"protocolversion"`
|
||||
WalletVersion int32 `json:"walletversion"`
|
||||
Balance float64 `json:"balance"`
|
||||
Blocks int32 `json:"blocks"`
|
||||
TimeOffset int64 `json:"timeoffset"`
|
||||
Connections int32 `json:"connections"`
|
||||
Proxy string `json:"proxy"`
|
||||
Difficulty float64 `json:"difficulty"`
|
||||
TestNet bool `json:"testnet"`
|
||||
KeypoolOldest int64 `json:"keypoololdest"`
|
||||
KeypoolSize int32 `json:"keypoolsize"`
|
||||
UnlockedUntil int64 `json:"unlocked_until"`
|
||||
PaytxFee float64 `json:"paytxfee"`
|
||||
RelayFee float64 `json:"relayfee"`
|
||||
Errors string `json:"errors"`
|
||||
}
|
||||
|
||||
// ListTransactionsResult models the data from the listtransactions command.
|
||||
type ListTransactionsResult struct {
|
||||
Abandoned bool `json:"abandoned"`
|
||||
Account string `json:"account"`
|
||||
Address string `json:"address,omitempty"`
|
||||
Amount float64 `json:"amount"`
|
||||
BIP125Replaceable string `json:"bip125-replaceable,omitempty"`
|
||||
BlockHash string `json:"blockhash,omitempty"`
|
||||
BlockIndex *int64 `json:"blockindex,omitempty"`
|
||||
BlockTime int64 `json:"blocktime,omitempty"`
|
||||
Category string `json:"category"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Fee *float64 `json:"fee,omitempty"`
|
||||
Generated bool `json:"generated,omitempty"`
|
||||
InvolvesWatchOnly bool `json:"involveswatchonly,omitempty"`
|
||||
Time int64 `json:"time"`
|
||||
TimeReceived int64 `json:"timereceived"`
|
||||
Trusted bool `json:"trusted"`
|
||||
TxID string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
WalletConflicts []string `json:"walletconflicts"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
OtherAccount string `json:"otheraccount,omitempty"`
|
||||
}
|
||||
|
||||
// ListReceivedByAccountResult models the data from the listreceivedbyaccount
|
||||
// command.
|
||||
type ListReceivedByAccountResult struct {
|
||||
Account string `json:"account"`
|
||||
Amount float64 `json:"amount"`
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
}
|
||||
|
||||
// ListReceivedByAddressResult models the data from the listreceivedbyaddress
|
||||
// command.
|
||||
type ListReceivedByAddressResult struct {
|
||||
Account string `json:"account"`
|
||||
Address string `json:"address"`
|
||||
Amount float64 `json:"amount"`
|
||||
Confirmations uint64 `json:"confirmations"`
|
||||
TxIDs []string `json:"txids,omitempty"`
|
||||
InvolvesWatchonly bool `json:"involvesWatchonly,omitempty"`
|
||||
}
|
||||
|
||||
// ListSinceBlockResult models the data from the listsinceblock command.
|
||||
type ListSinceBlockResult struct {
|
||||
Transactions []ListTransactionsResult `json:"transactions"`
|
||||
LastBlock string `json:"lastblock"`
|
||||
}
|
||||
|
||||
// ListUnspentResult models a successful response from the listunspent request.
|
||||
type ListUnspentResult struct {
|
||||
TxID string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
Address string `json:"address"`
|
||||
Account string `json:"account"`
|
||||
ScriptPubKey string `json:"scriptPubKey"`
|
||||
RedeemScript string `json:"redeemScript,omitempty"`
|
||||
Amount float64 `json:"amount"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
Spendable bool `json:"spendable"`
|
||||
}
|
||||
|
||||
// SignRawTransactionError models the data that contains script verification
|
||||
// errors from the signrawtransaction request.
|
||||
type SignRawTransactionError struct {
|
||||
TxID string `json:"txid"`
|
||||
Vout uint32 `json:"vout"`
|
||||
ScriptSig string `json:"scriptSig"`
|
||||
Sequence uint32 `json:"sequence"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// SignRawTransactionResult models the data from the signrawtransaction
|
||||
// command.
|
||||
type SignRawTransactionResult struct {
|
||||
Hex string `json:"hex"`
|
||||
Complete bool `json:"complete"`
|
||||
Errors []SignRawTransactionError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// ValidateAddressWalletResult models the data returned by the wallet server
|
||||
// validateaddress command.
|
||||
type ValidateAddressWalletResult struct {
|
||||
IsValid bool `json:"isvalid"`
|
||||
Address string `json:"address,omitempty"`
|
||||
IsMine bool `json:"ismine,omitempty"`
|
||||
IsWatchOnly bool `json:"iswatchonly,omitempty"`
|
||||
IsScript bool `json:"isscript,omitempty"`
|
||||
PubKey string `json:"pubkey,omitempty"`
|
||||
IsCompressed bool `json:"iscompressed,omitempty"`
|
||||
Account string `json:"account,omitempty"`
|
||||
Addresses []string `json:"addresses,omitempty"`
|
||||
Hex string `json:"hex,omitempty"`
|
||||
Script string `json:"script,omitempty"`
|
||||
SigsRequired int32 `json:"sigsrequired,omitempty"`
|
||||
}
|
||||
|
||||
// GetBestBlockResult models the data from the getbestblock command.
|
||||
type GetBestBlockResult struct {
|
||||
Hash string `json:"hash"`
|
||||
Height int32 `json:"height"`
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package btcjson
|
||||
|
||||
// NOTE: This file is intended to house the RPC commands that are supported by
|
||||
// a wallet server, but are only available via websockets.
|
||||
|
||||
// CreateEncryptedWalletCmd defines the createencryptedwallet JSON-RPC command.
|
||||
type CreateEncryptedWalletCmd struct {
|
||||
Passphrase string
|
||||
}
|
||||
|
||||
// NewCreateEncryptedWalletCmd returns a new instance which can be used to issue
|
||||
// a createencryptedwallet JSON-RPC command.
|
||||
func NewCreateEncryptedWalletCmd(passphrase string) *CreateEncryptedWalletCmd {
|
||||
return &CreateEncryptedWalletCmd{
|
||||
Passphrase: passphrase,
|
||||
}
|
||||
}
|
||||
|
||||
// ExportWatchingWalletCmd defines the exportwatchingwallet JSON-RPC command.
|
||||
type ExportWatchingWalletCmd struct {
|
||||
Account *string
|
||||
Download *bool `jsonrpcdefault:"false"`
|
||||
}
|
||||
|
||||
// NewExportWatchingWalletCmd returns a new instance which can be used to issue
|
||||
// a exportwatchingwallet JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewExportWatchingWalletCmd(account *string, download *bool) *ExportWatchingWalletCmd {
|
||||
return &ExportWatchingWalletCmd{
|
||||
Account: account,
|
||||
Download: download,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUnconfirmedBalanceCmd defines the getunconfirmedbalance JSON-RPC command.
|
||||
type GetUnconfirmedBalanceCmd struct {
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewGetUnconfirmedBalanceCmd returns a new instance which can be used to issue
|
||||
// a getunconfirmedbalance JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewGetUnconfirmedBalanceCmd(account *string) *GetUnconfirmedBalanceCmd {
|
||||
return &GetUnconfirmedBalanceCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAddressTransactionsCmd defines the listaddresstransactions JSON-RPC
|
||||
// command.
|
||||
type ListAddressTransactionsCmd struct {
|
||||
Addresses []string
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewListAddressTransactionsCmd returns a new instance which can be used to
|
||||
// issue a listaddresstransactions JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListAddressTransactionsCmd(addresses []string, account *string) *ListAddressTransactionsCmd {
|
||||
return &ListAddressTransactionsCmd{
|
||||
Addresses: addresses,
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// ListAllTransactionsCmd defines the listalltransactions JSON-RPC command.
|
||||
type ListAllTransactionsCmd struct {
|
||||
Account *string
|
||||
}
|
||||
|
||||
// NewListAllTransactionsCmd returns a new instance which can be used to issue a
|
||||
// listalltransactions JSON-RPC command.
|
||||
//
|
||||
// The parameters which are pointers indicate they are optional. Passing nil
|
||||
// for optional parameters will use the default value.
|
||||
func NewListAllTransactionsCmd(account *string) *ListAllTransactionsCmd {
|
||||
return &ListAllTransactionsCmd{
|
||||
Account: account,
|
||||
}
|
||||
}
|
||||
|
||||
// RecoverAddressesCmd defines the recoveraddresses JSON-RPC command.
|
||||
type RecoverAddressesCmd struct {
|
||||
Account string
|
||||
N int
|
||||
}
|
||||
|
||||
// NewRecoverAddressesCmd returns a new instance which can be used to issue a
|
||||
// recoveraddresses JSON-RPC command.
|
||||
func NewRecoverAddressesCmd(account string, n int) *RecoverAddressesCmd {
|
||||
return &RecoverAddressesCmd{
|
||||
Account: account,
|
||||
N: n,
|
||||
}
|
||||
}
|
||||
|
||||
// WalletIsLockedCmd defines the walletislocked JSON-RPC command.
|
||||
type WalletIsLockedCmd struct{}
|
||||
|
||||
// NewWalletIsLockedCmd returns a new instance which can be used to issue a
|
||||
// walletislocked JSON-RPC command.
|
||||
func NewWalletIsLockedCmd() *WalletIsLockedCmd {
|
||||
return &WalletIsLockedCmd{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable with a wallet server via
|
||||
// websockets.
|
||||
flags := UFWalletOnly | UFWebsocketOnly
|
||||
|
||||
MustRegisterCmd("createencryptedwallet", (*CreateEncryptedWalletCmd)(nil), flags)
|
||||
MustRegisterCmd("exportwatchingwallet", (*ExportWatchingWalletCmd)(nil), flags)
|
||||
MustRegisterCmd("getunconfirmedbalance", (*GetUnconfirmedBalanceCmd)(nil), flags)
|
||||
MustRegisterCmd("listaddresstransactions", (*ListAddressTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("listalltransactions", (*ListAllTransactionsCmd)(nil), flags)
|
||||
MustRegisterCmd("recoveraddresses", (*RecoverAddressesCmd)(nil), flags)
|
||||
MustRegisterCmd("walletislocked", (*WalletIsLockedCmd)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright (c) 2014 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// NOTE: This file is intended to house the RPC websocket notifications that are
|
||||
// supported by a wallet server.
|
||||
|
||||
package btcjson
|
||||
|
||||
const (
|
||||
// AccountBalanceNtfnMethod is the method used for account balance
|
||||
// notifications.
|
||||
AccountBalanceNtfnMethod = "accountbalance"
|
||||
|
||||
// BtcdConnectedNtfnMethod is the method used for notifications when
|
||||
// a wallet server is connected to a chain server.
|
||||
BtcdConnectedNtfnMethod = "btcdconnected"
|
||||
|
||||
// WalletLockStateNtfnMethod is the method used to notify the lock state
|
||||
// of a wallet has changed.
|
||||
WalletLockStateNtfnMethod = "walletlockstate"
|
||||
|
||||
// NewTxNtfnMethod is the method used to notify that a wallet server has
|
||||
// added a new transaction to the transaction store.
|
||||
NewTxNtfnMethod = "newtx"
|
||||
)
|
||||
|
||||
// AccountBalanceNtfn defines the accountbalance JSON-RPC notification.
|
||||
type AccountBalanceNtfn struct {
|
||||
Account string
|
||||
Balance float64 // In BTC
|
||||
Confirmed bool // Whether Balance is confirmed or unconfirmed.
|
||||
}
|
||||
|
||||
// NewAccountBalanceNtfn returns a new instance which can be used to issue an
|
||||
// accountbalance JSON-RPC notification.
|
||||
func NewAccountBalanceNtfn(account string, balance float64, confirmed bool) *AccountBalanceNtfn {
|
||||
return &AccountBalanceNtfn{
|
||||
Account: account,
|
||||
Balance: balance,
|
||||
Confirmed: confirmed,
|
||||
}
|
||||
}
|
||||
|
||||
// BtcdConnectedNtfn defines the btcdconnected JSON-RPC notification.
|
||||
type BtcdConnectedNtfn struct {
|
||||
Connected bool
|
||||
}
|
||||
|
||||
// NewBtcdConnectedNtfn returns a new instance which can be used to issue a
|
||||
// btcdconnected JSON-RPC notification.
|
||||
func NewBtcdConnectedNtfn(connected bool) *BtcdConnectedNtfn {
|
||||
return &BtcdConnectedNtfn{
|
||||
Connected: connected,
|
||||
}
|
||||
}
|
||||
|
||||
// WalletLockStateNtfn defines the walletlockstate JSON-RPC notification.
|
||||
type WalletLockStateNtfn struct {
|
||||
Locked bool
|
||||
}
|
||||
|
||||
// NewWalletLockStateNtfn returns a new instance which can be used to issue a
|
||||
// walletlockstate JSON-RPC notification.
|
||||
func NewWalletLockStateNtfn(locked bool) *WalletLockStateNtfn {
|
||||
return &WalletLockStateNtfn{
|
||||
Locked: locked,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTxNtfn defines the newtx JSON-RPC notification.
|
||||
type NewTxNtfn struct {
|
||||
Account string
|
||||
Details ListTransactionsResult
|
||||
}
|
||||
|
||||
// NewNewTxNtfn returns a new instance which can be used to issue a newtx
|
||||
// JSON-RPC notification.
|
||||
func NewNewTxNtfn(account string, details ListTransactionsResult) *NewTxNtfn {
|
||||
return &NewTxNtfn{
|
||||
Account: account,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The commands in this file are only usable with a wallet server via
|
||||
// websockets and are notifications.
|
||||
flags := UFWalletOnly | UFWebsocketOnly | UFNotification
|
||||
|
||||
MustRegisterCmd(AccountBalanceNtfnMethod, (*AccountBalanceNtfn)(nil), flags)
|
||||
MustRegisterCmd(BtcdConnectedNtfnMethod, (*BtcdConnectedNtfn)(nil), flags)
|
||||
MustRegisterCmd(WalletLockStateNtfnMethod, (*WalletLockStateNtfn)(nil), flags)
|
||||
MustRegisterCmd(NewTxNtfnMethod, (*NewTxNtfn)(nil), flags)
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
chaincfg
|
||||
========
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/chaincfg)
|
||||
|
||||
Package chaincfg defines chain configuration parameters for the three standard
|
||||
Bitcoin networks and provides the ability for callers to define their own custom
|
||||
Bitcoin networks.
|
||||
|
||||
Although this package was primarily written for btcd, it has intentionally been
|
||||
designed so it can be used as a standalone package for any projects needing to
|
||||
use parameters for the standard Bitcoin networks or for projects needing to
|
||||
define their own network.
|
||||
|
||||
## Sample Use
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
)
|
||||
|
||||
var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network")
|
||||
|
||||
// By default (without -testnet), use mainnet.
|
||||
var chainParams = &chaincfg.MainNetParams
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// Modify active network parameters if operating on testnet.
|
||||
if *testnet {
|
||||
chainParams = &chaincfg.TestNet3Params
|
||||
}
|
||||
|
||||
// later...
|
||||
|
||||
// Create and print new payment address, specific to the active network.
|
||||
pubKeyHash := make([]byte, 20)
|
||||
addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainParams)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(addr)
|
||||
}
|
||||
```
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/chaincfg
|
||||
```
|
||||
|
||||
## GPG Verification Key
|
||||
|
||||
All official release tags are signed by Conformal so users can ensure the code
|
||||
has not been tampered with and is coming from the btcsuite developers. To
|
||||
verify the signature perform the following:
|
||||
|
||||
- Download the public key from the Conformal website at
|
||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
||||
|
||||
- Import the public key into your GPG keyring:
|
||||
```bash
|
||||
gpg --import GIT-GPG-KEY-conformal.txt
|
||||
```
|
||||
|
||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
||||
placeholder for the specific tag:
|
||||
```bash
|
||||
git tag -v TAG_NAME
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package chaincfg is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
|
@ -0,0 +1,41 @@
|
|||
chainhash
|
||||
=========
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/chaincfg/chainhash)
|
||||
=======
|
||||
|
||||
chainhash provides a generic hash type and associated functions that allows the
|
||||
specific hash algorithm to be abstracted.
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/chaincfg/chainhash
|
||||
```
|
||||
|
||||
## GPG Verification Key
|
||||
|
||||
All official release tags are signed by Conformal so users can ensure the code
|
||||
has not been tampered with and is coming from the btcsuite developers. To
|
||||
verify the signature perform the following:
|
||||
|
||||
- Download the public key from the Conformal website at
|
||||
https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt
|
||||
|
||||
- Import the public key into your GPG keyring:
|
||||
```bash
|
||||
gpg --import GIT-GPG-KEY-conformal.txt
|
||||
```
|
||||
|
||||
- Verify the release tag with the following command where `TAG_NAME` is a
|
||||
placeholder for the specific tag:
|
||||
```bash
|
||||
git tag -v TAG_NAME
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package chainhash is licensed under the [copyfree](http://copyfree.org) ISC
|
||||
License.
|
|
@ -0,0 +1,5 @@
|
|||
// Package chainhash provides abstracted hash functionality.
|
||||
//
|
||||
// This package provides a generic hash type and associated functions that
|
||||
// allows the specific hash algorithm to be abstracted.
|
||||
package chainhash
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright (c) 2013-2016 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 chainhash
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// HashSize of array used to store hashes. See Hash.
|
||||
const HashSize = 32
|
||||
|
||||
// MaxHashStringSize is the maximum length of a Hash hash string.
|
||||
const MaxHashStringSize = HashSize * 2
|
||||
|
||||
// ErrHashStrSize describes an error that indicates the caller specified a hash
|
||||
// string that has too many characters.
|
||||
var ErrHashStrSize = fmt.Errorf("max hash string length is %v bytes", MaxHashStringSize)
|
||||
|
||||
// Hash is used in several of the bitcoin messages and common structures. It
|
||||
// typically represents the double sha256 of data.
|
||||
type Hash [HashSize]byte
|
||||
|
||||
// String returns the Hash as the hexadecimal string of the byte-reversed
|
||||
// hash.
|
||||
func (hash Hash) String() string {
|
||||
for i := 0; i < HashSize/2; i++ {
|
||||
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
|
||||
}
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// CloneBytes returns a copy of the bytes which represent the hash as a byte
|
||||
// slice.
|
||||
//
|
||||
// NOTE: It is generally cheaper to just slice the hash directly thereby reusing
|
||||
// the same bytes rather than calling this method.
|
||||
func (hash *Hash) CloneBytes() []byte {
|
||||
newHash := make([]byte, HashSize)
|
||||
copy(newHash, hash[:])
|
||||
|
||||
return newHash
|
||||
}
|
||||
|
||||
// SetBytes sets the bytes which represent the hash. An error is returned if
|
||||
// the number of bytes passed in is not HashSize.
|
||||
func (hash *Hash) SetBytes(newHash []byte) error {
|
||||
nhlen := len(newHash)
|
||||
if nhlen != HashSize {
|
||||
return fmt.Errorf("invalid hash length of %v, want %v", nhlen,
|
||||
HashSize)
|
||||
}
|
||||
copy(hash[:], newHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEqual returns true if target is the same as hash.
|
||||
func (hash *Hash) IsEqual(target *Hash) bool {
|
||||
if hash == nil && target == nil {
|
||||
return true
|
||||
}
|
||||
if hash == nil || target == nil {
|
||||
return false
|
||||
}
|
||||
return *hash == *target
|
||||
}
|
||||
|
||||
// NewHash returns a new Hash from a byte slice. An error is returned if
|
||||
// the number of bytes passed in is not HashSize.
|
||||
func NewHash(newHash []byte) (*Hash, error) {
|
||||
var sh Hash
|
||||
err := sh.SetBytes(newHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &sh, err
|
||||
}
|
||||
|
||||
// NewHashFromStr creates a Hash from a hash string. The string should be
|
||||
// the hexadecimal string of a byte-reversed hash, but any missing characters
|
||||
// result in zero padding at the end of the Hash.
|
||||
func NewHashFromStr(hash string) (*Hash, error) {
|
||||
ret := new(Hash)
|
||||
err := Decode(ret, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a
|
||||
// destination.
|
||||
func Decode(dst *Hash, src string) error {
|
||||
// Return error if hash string is too long.
|
||||
if len(src) > MaxHashStringSize {
|
||||
return ErrHashStrSize
|
||||
}
|
||||
|
||||
// Hex decoder expects the hash to be a multiple of two. When not, pad
|
||||
// with a leading zero.
|
||||
var srcBytes []byte
|
||||
if len(src)%2 == 0 {
|
||||
srcBytes = []byte(src)
|
||||
} else {
|
||||
srcBytes = make([]byte, 1+len(src))
|
||||
srcBytes[0] = '0'
|
||||
copy(srcBytes[1:], src)
|
||||
}
|
||||
|
||||
// Hex decode the source bytes to a temporary destination.
|
||||
var reversedHash Hash
|
||||
_, err := hex.Decode(reversedHash[HashSize-hex.DecodedLen(len(srcBytes)):], srcBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reverse copy from the temporary hash to destination. Because the
|
||||
// temporary was zeroed, the written result will be correctly padded.
|
||||
for i, b := range reversedHash[:HashSize/2] {
|
||||
dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2015 The Decred developers
|
||||
// Copyright (c) 2016-2017 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chainhash
|
||||
|
||||
import "crypto/sha256"
|
||||
|
||||
// HashB calculates hash(b) and returns the resulting bytes.
|
||||
func HashB(b []byte) []byte {
|
||||
hash := sha256.Sum256(b)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
// HashH calculates hash(b) and returns the resulting bytes as a Hash.
|
||||
func HashH(b []byte) Hash {
|
||||
return Hash(sha256.Sum256(b))
|
||||
}
|
||||
|
||||
// DoubleHashB calculates hash(hash(b)) and returns the resulting bytes.
|
||||
func DoubleHashB(b []byte) []byte {
|
||||
first := sha256.Sum256(b)
|
||||
second := sha256.Sum256(first[:])
|
||||
return second[:]
|
||||
}
|
||||
|
||||
// DoubleHashH calculates hash(hash(b)) and returns the resulting bytes as a
|
||||
// Hash.
|
||||
func DoubleHashH(b []byte) Hash {
|
||||
first := sha256.Sum256(b)
|
||||
return Hash(sha256.Sum256(first[:]))
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Package chaincfg defines chain configuration parameters.
|
||||
//
|
||||
// In addition to the main Bitcoin network, which is intended for the transfer
|
||||
// of monetary value, there also exists two currently active standard networks:
|
||||
// regression test and testnet (version 3). These networks are incompatible
|
||||
// with each other (each sharing a different genesis block) and software should
|
||||
// handle errors where input intended for one network is used on an application
|
||||
// instance running on a different network.
|
||||
//
|
||||
// For library packages, chaincfg provides the ability to lookup chain
|
||||
// parameters and encoding magics when passed a *Params. Older APIs not updated
|
||||
// to the new convention of passing a *Params may lookup the parameters for a
|
||||
// wire.BitcoinNet using ParamsForNet, but be aware that this usage is
|
||||
// deprecated and will be removed from chaincfg in the future.
|
||||
//
|
||||
// For main packages, a (typically global) var may be assigned the address of
|
||||
// one of the standard Param vars for use as the application's "active" network.
|
||||
// When a network parameter is needed, it may then be looked up through this
|
||||
// variable (either directly, or hidden in a library call).
|
||||
//
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "flag"
|
||||
// "fmt"
|
||||
// "log"
|
||||
//
|
||||
// "github.com/btcsuite/btcutil"
|
||||
// "github.com/btcsuite/btcd/chaincfg"
|
||||
// )
|
||||
//
|
||||
// var testnet = flag.Bool("testnet", false, "operate on the testnet Bitcoin network")
|
||||
//
|
||||
// // By default (without -testnet), use mainnet.
|
||||
// var chainParams = &chaincfg.MainNetParams
|
||||
//
|
||||
// func main() {
|
||||
// flag.Parse()
|
||||
//
|
||||
// // Modify active network parameters if operating on testnet.
|
||||
// if *testnet {
|
||||
// chainParams = &chaincfg.TestNet3Params
|
||||
// }
|
||||
//
|
||||
// // later...
|
||||
//
|
||||
// // Create and print new payment address, specific to the active network.
|
||||
// pubKeyHash := make([]byte, 20)
|
||||
// addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainParams)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// fmt.Println(addr)
|
||||
// }
|
||||
//
|
||||
// If an application does not use one of the three standard Bitcoin networks,
|
||||
// a new Params struct may be created which defines the parameters for the
|
||||
// non-standard network. As a general rule of thumb, all network parameters
|
||||
// should be unique to the network, but parameter collisions can still occur
|
||||
// (unfortunately, this is the case with regtest and testnet3 sharing magics).
|
||||
package chaincfg
|
|
@ -0,0 +1,172 @@
|
|||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chaincfg
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
|
||||
// the main network, regression test network, and test network (version 3).
|
||||
var genesisCoinbaseTx = wire.MsgTx{
|
||||
Version: 1,
|
||||
TxIn: []*wire.TxIn{
|
||||
{
|
||||
PreviousOutPoint: wire.OutPoint{
|
||||
Hash: chainhash.Hash{},
|
||||
Index: 0xffffffff,
|
||||
},
|
||||
SignatureScript: []byte{
|
||||
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x45, /* |.......E| */
|
||||
0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65, /* |The Time| */
|
||||
0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, 0x6e, /* |s 03/Jan| */
|
||||
0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, 0x68, /* |/2009 Ch| */
|
||||
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, 0x72, /* |ancellor| */
|
||||
0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, 0x6e, /* | on brin| */
|
||||
0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, 0x63, /* |k of sec|*/
|
||||
0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, 0x6c, /* |ond bail| */
|
||||
0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, /* |out for |*/
|
||||
0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */
|
||||
},
|
||||
Sequence: 0xffffffff,
|
||||
},
|
||||
},
|
||||
TxOut: []*wire.TxOut{
|
||||
{
|
||||
Value: 0x12a05f200,
|
||||
PkScript: []byte{
|
||||
0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
|
||||
0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
|
||||
0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
|
||||
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
|
||||
0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */
|
||||
0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */
|
||||
0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */
|
||||
0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */
|
||||
0x1d, 0x5f, 0xac, /* |._.| */
|
||||
},
|
||||
},
|
||||
},
|
||||
LockTime: 0,
|
||||
}
|
||||
|
||||
// genesisHash is the hash of the first block in the block chain for the main
|
||||
// network (genesis block).
|
||||
var genesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72,
|
||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
})
|
||||
|
||||
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
||||
// for the main network.
|
||||
var genesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2,
|
||||
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
||||
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
||||
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
|
||||
})
|
||||
|
||||
// genesisBlock defines the genesis block of the block chain which serves as the
|
||||
// public transaction ledger for the main network.
|
||||
var genesisBlock = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||
MerkleRoot: genesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
||||
Timestamp: time.Unix(0x495fab29, 0), // 2009-01-03 18:15:05 +0000 UTC
|
||||
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
||||
Nonce: 0x7c2bac1d, // 2083236893
|
||||
},
|
||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||
}
|
||||
|
||||
// regTestGenesisHash is the hash of the first block in the block chain for the
|
||||
// regression test network (genesis block).
|
||||
var regTestGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59,
|
||||
0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, 0x5b, 0xbf,
|
||||
0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f,
|
||||
0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f,
|
||||
})
|
||||
|
||||
// regTestGenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||
// block for the regression test network. It is the same as the merkle root for
|
||||
// the main network.
|
||||
var regTestGenesisMerkleRoot = genesisMerkleRoot
|
||||
|
||||
// regTestGenesisBlock defines the genesis block of the block chain which serves
|
||||
// as the public transaction ledger for the regression test network.
|
||||
var regTestGenesisBlock = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||
MerkleRoot: regTestGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
||||
Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC
|
||||
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
|
||||
Nonce: 2,
|
||||
},
|
||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||
}
|
||||
|
||||
// testNet3GenesisHash is the hash of the first block in the block chain for the
|
||||
// test network (version 3).
|
||||
var testNet3GenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71,
|
||||
0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce, 0xc3, 0xae,
|
||||
0xba, 0x79, 0x97, 0x20, 0x84, 0xe9, 0x0e, 0xad,
|
||||
0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00,
|
||||
})
|
||||
|
||||
// testNet3GenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||
// block for the test network (version 3). It is the same as the merkle root
|
||||
// for the main network.
|
||||
var testNet3GenesisMerkleRoot = genesisMerkleRoot
|
||||
|
||||
// testNet3GenesisBlock defines the genesis block of the block chain which
|
||||
// serves as the public transaction ledger for the test network (version 3).
|
||||
var testNet3GenesisBlock = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||
MerkleRoot: testNet3GenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
||||
Timestamp: time.Unix(1296688602, 0), // 2011-02-02 23:16:42 +0000 UTC
|
||||
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
||||
Nonce: 0x18aea41a, // 414098458
|
||||
},
|
||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||
}
|
||||
|
||||
// simNetGenesisHash is the hash of the first block in the block chain for the
|
||||
// simulation test network.
|
||||
var simNetGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
|
||||
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a,
|
||||
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||
})
|
||||
|
||||
// simNetGenesisMerkleRoot is the hash of the first transaction in the genesis
|
||||
// block for the simulation test network. It is the same as the merkle root for
|
||||
// the main network.
|
||||
var simNetGenesisMerkleRoot = genesisMerkleRoot
|
||||
|
||||
// simNetGenesisBlock defines the genesis block of the block chain which serves
|
||||
// as the public transaction ledger for the simulation test network.
|
||||
var simNetGenesisBlock = wire.MsgBlock{
|
||||
Header: wire.BlockHeader{
|
||||
Version: 1,
|
||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
||||
MerkleRoot: simNetGenesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
||||
Timestamp: time.Unix(1401292357, 0), // 2014-05-28 15:52:37 +0000 UTC
|
||||
Bits: 0x207fffff, // 545259519 [7fffff0000000000000000000000000000000000000000000000000000000000]
|
||||
Nonce: 2,
|
||||
},
|
||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx},
|
||||
}
|
|
@ -0,0 +1,712 @@
|
|||
// Copyright (c) 2014-2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package chaincfg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
// These variables are the chain proof-of-work limit parameters for each default
|
||||
// network.
|
||||
var (
|
||||
// bigOne is 1 represented as a big.Int. It is defined here to avoid
|
||||
// the overhead of creating it multiple times.
|
||||
bigOne = big.NewInt(1)
|
||||
|
||||
// mainPowLimit is the highest proof of work value a Bitcoin block can
|
||||
// have for the main network. It is the value 2^224 - 1.
|
||||
mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
|
||||
|
||||
// regressionPowLimit is the highest proof of work value a Bitcoin block
|
||||
// can have for the regression test network. It is the value 2^255 - 1.
|
||||
regressionPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
|
||||
|
||||
// testNet3PowLimit is the highest proof of work value a Bitcoin block
|
||||
// can have for the test network (version 3). It is the value
|
||||
// 2^224 - 1.
|
||||
testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
|
||||
|
||||
// simNetPowLimit is the highest proof of work value a Bitcoin block
|
||||
// can have for the simulation test network. It is the value 2^255 - 1.
|
||||
simNetPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 255), bigOne)
|
||||
)
|
||||
|
||||
// Checkpoint identifies a known good point in the block chain. Using
|
||||
// checkpoints allows a few optimizations for old blocks during initial download
|
||||
// and also prevents forks from old blocks.
|
||||
//
|
||||
// Each checkpoint is selected based upon several factors. See the
|
||||
// documentation for blockchain.IsCheckpointCandidate for details on the
|
||||
// selection criteria.
|
||||
type Checkpoint struct {
|
||||
Height int32
|
||||
Hash *chainhash.Hash
|
||||
}
|
||||
|
||||
// DNSSeed identifies a DNS seed.
|
||||
type DNSSeed struct {
|
||||
// Host defines the hostname of the seed.
|
||||
Host string
|
||||
|
||||
// HasFiltering defines whether the seed supports filtering
|
||||
// by service flags (wire.ServiceFlag).
|
||||
HasFiltering bool
|
||||
}
|
||||
|
||||
// ConsensusDeployment defines details related to a specific consensus rule
|
||||
// change that is voted in. This is part of BIP0009.
|
||||
type ConsensusDeployment struct {
|
||||
// BitNumber defines the specific bit number within the block version
|
||||
// this particular soft-fork deployment refers to.
|
||||
BitNumber uint8
|
||||
|
||||
// StartTime is the median block time after which voting on the
|
||||
// deployment starts.
|
||||
StartTime uint64
|
||||
|
||||
// ExpireTime is the median block time after which the attempted
|
||||
// deployment expires.
|
||||
ExpireTime uint64
|
||||
}
|
||||
|
||||
// Constants that define the deployment offset in the deployments field of the
|
||||
// parameters for each deployment. This is useful to be able to get the details
|
||||
// of a specific deployment by name.
|
||||
const (
|
||||
// DeploymentTestDummy defines the rule change deployment ID for testing
|
||||
// purposes.
|
||||
DeploymentTestDummy = iota
|
||||
|
||||
// DeploymentCSV defines the rule change deployment ID for the CSV
|
||||
// soft-fork package. The CSV package includes the deployment of BIPS
|
||||
// 68, 112, and 113.
|
||||
DeploymentCSV
|
||||
|
||||
// DeploymentSegwit defines the rule change deployment ID for the
|
||||
// Segregated Witness (segwit) soft-fork package. The segwit package
|
||||
// includes the deployment of BIPS 141, 142, 144, 145, 147 and 173.
|
||||
DeploymentSegwit
|
||||
|
||||
// NOTE: DefinedDeployments must always come last since it is used to
|
||||
// determine how many defined deployments there currently are.
|
||||
|
||||
// DefinedDeployments is the number of currently defined deployments.
|
||||
DefinedDeployments
|
||||
)
|
||||
|
||||
// Params defines a Bitcoin network by its parameters. These parameters may be
|
||||
// used by Bitcoin applications to differentiate networks as well as addresses
|
||||
// and keys for one network from those intended for use on another network.
|
||||
type Params struct {
|
||||
// Name defines a human-readable identifier for the network.
|
||||
Name string
|
||||
|
||||
// Net defines the magic bytes used to identify the network.
|
||||
Net wire.BitcoinNet
|
||||
|
||||
// DefaultPort defines the default peer-to-peer port for the network.
|
||||
DefaultPort string
|
||||
|
||||
// DNSSeeds defines a list of DNS seeds for the network that are used
|
||||
// as one method to discover peers.
|
||||
DNSSeeds []DNSSeed
|
||||
|
||||
// GenesisBlock defines the first block of the chain.
|
||||
GenesisBlock *wire.MsgBlock
|
||||
|
||||
// GenesisHash is the starting block hash.
|
||||
GenesisHash *chainhash.Hash
|
||||
|
||||
// PowLimit defines the highest allowed proof of work value for a block
|
||||
// as a uint256.
|
||||
PowLimit *big.Int
|
||||
|
||||
// PowLimitBits defines the highest allowed proof of work value for a
|
||||
// block in compact form.
|
||||
PowLimitBits uint32
|
||||
|
||||
// These fields define the block heights at which the specified softfork
|
||||
// BIP became active.
|
||||
BIP0034Height int32
|
||||
BIP0065Height int32
|
||||
BIP0066Height int32
|
||||
|
||||
// CoinbaseMaturity is the number of blocks required before newly mined
|
||||
// coins (coinbase transactions) can be spent.
|
||||
CoinbaseMaturity uint16
|
||||
|
||||
// SubsidyReductionInterval is the interval of blocks before the subsidy
|
||||
// is reduced.
|
||||
SubsidyReductionInterval int32
|
||||
|
||||
// TargetTimespan is the desired amount of time that should elapse
|
||||
// before the block difficulty requirement is examined to determine how
|
||||
// it should be changed in order to maintain the desired block
|
||||
// generation rate.
|
||||
TargetTimespan time.Duration
|
||||
|
||||
// TargetTimePerBlock is the desired amount of time to generate each
|
||||
// block.
|
||||
TargetTimePerBlock time.Duration
|
||||
|
||||
// RetargetAdjustmentFactor is the adjustment factor used to limit
|
||||
// the minimum and maximum amount of adjustment that can occur between
|
||||
// difficulty retargets.
|
||||
RetargetAdjustmentFactor int64
|
||||
|
||||
// ReduceMinDifficulty defines whether the network should reduce the
|
||||
// minimum required difficulty after a long enough period of time has
|
||||
// passed without finding a block. This is really only useful for test
|
||||
// networks and should not be set on a main network.
|
||||
ReduceMinDifficulty bool
|
||||
|
||||
// MinDiffReductionTime is the amount of time after which the minimum
|
||||
// required difficulty should be reduced when a block hasn't been found.
|
||||
//
|
||||
// NOTE: This only applies if ReduceMinDifficulty is true.
|
||||
MinDiffReductionTime time.Duration
|
||||
|
||||
// GenerateSupported specifies whether or not CPU mining is allowed.
|
||||
GenerateSupported bool
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints []Checkpoint
|
||||
|
||||
// These fields are related to voting on consensus rule changes as
|
||||
// defined by BIP0009.
|
||||
//
|
||||
// RuleChangeActivationThreshold is the number of blocks in a threshold
|
||||
// state retarget window for which a positive vote for a rule change
|
||||
// must be cast in order to lock in a rule change. It should typically
|
||||
// be 95% for the main network and 75% for test networks.
|
||||
//
|
||||
// MinerConfirmationWindow is the number of blocks in each threshold
|
||||
// state retarget window.
|
||||
//
|
||||
// Deployments define the specific consensus rule changes to be voted
|
||||
// on.
|
||||
RuleChangeActivationThreshold uint32
|
||||
MinerConfirmationWindow uint32
|
||||
Deployments [DefinedDeployments]ConsensusDeployment
|
||||
|
||||
// Mempool parameters
|
||||
RelayNonStdTxs bool
|
||||
|
||||
// Human-readable part for Bech32 encoded segwit addresses, as defined
|
||||
// in BIP 173.
|
||||
Bech32HRPSegwit string
|
||||
|
||||
// Address encoding magics
|
||||
PubKeyHashAddrID byte // First byte of a P2PKH address
|
||||
ScriptHashAddrID byte // First byte of a P2SH address
|
||||
PrivateKeyID byte // First byte of a WIF private key
|
||||
WitnessPubKeyHashAddrID byte // First byte of a P2WPKH address
|
||||
WitnessScriptHashAddrID byte // First byte of a P2WSH address
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDPrivateKeyID [4]byte
|
||||
HDPublicKeyID [4]byte
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType uint32
|
||||
}
|
||||
|
||||
// MainNetParams defines the network parameters for the main Bitcoin network.
|
||||
var MainNetParams = Params{
|
||||
Name: "mainnet",
|
||||
Net: wire.MainNet,
|
||||
DefaultPort: "8333",
|
||||
DNSSeeds: []DNSSeed{
|
||||
{"seed.bitcoin.sipa.be", true},
|
||||
{"dnsseed.bluematt.me", true},
|
||||
{"dnsseed.bitcoin.dashjr.org", false},
|
||||
{"seed.bitcoinstats.com", true},
|
||||
{"seed.bitnodes.io", false},
|
||||
{"seed.bitcoin.jonasschnelli.ch", true},
|
||||
},
|
||||
|
||||
// Chain parameters
|
||||
GenesisBlock: &genesisBlock,
|
||||
GenesisHash: &genesisHash,
|
||||
PowLimit: mainPowLimit,
|
||||
PowLimitBits: 0x1d00ffff,
|
||||
BIP0034Height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
|
||||
BIP0065Height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
|
||||
BIP0066Height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
||||
CoinbaseMaturity: 100,
|
||||
SubsidyReductionInterval: 210000,
|
||||
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
||||
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
||||
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
||||
ReduceMinDifficulty: false,
|
||||
MinDiffReductionTime: 0,
|
||||
GenerateSupported: false,
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints: []Checkpoint{
|
||||
{11111, newHashFromStr("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")},
|
||||
{33333, newHashFromStr("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")},
|
||||
{74000, newHashFromStr("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")},
|
||||
{105000, newHashFromStr("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")},
|
||||
{134444, newHashFromStr("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")},
|
||||
{168000, newHashFromStr("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")},
|
||||
{193000, newHashFromStr("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")},
|
||||
{210000, newHashFromStr("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")},
|
||||
{216116, newHashFromStr("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")},
|
||||
{225430, newHashFromStr("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")},
|
||||
{250000, newHashFromStr("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")},
|
||||
{267300, newHashFromStr("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac")},
|
||||
{279000, newHashFromStr("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")},
|
||||
{300255, newHashFromStr("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2")},
|
||||
{319400, newHashFromStr("000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b")},
|
||||
{343185, newHashFromStr("0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554")},
|
||||
{352940, newHashFromStr("000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff")},
|
||||
{382320, newHashFromStr("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2")},
|
||||
{400000, newHashFromStr("000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f")},
|
||||
{430000, newHashFromStr("000000000000000001868b2bb3a285f3cc6b33ea234eb70facf4dcdf22186b87")},
|
||||
{460000, newHashFromStr("000000000000000000ef751bbce8e744ad303c47ece06c8d863e4d417efc258c")},
|
||||
{490000, newHashFromStr("000000000000000000de069137b17b8d5a3dfbd5b145b2dcfb203f15d0c4de90")},
|
||||
{520000, newHashFromStr("0000000000000000000d26984c0229c9f6962dc74db0a6d525f2f1640396f69c")},
|
||||
{550000, newHashFromStr("000000000000000000223b7a2298fb1c6c75fb0efc28a4c56853ff4112ec6bc9")},
|
||||
{560000, newHashFromStr("0000000000000000002c7b276daf6efb2b6aa68e2ce3be67ef925b3264ae7122")},
|
||||
},
|
||||
|
||||
// Consensus rule change deployments.
|
||||
//
|
||||
// The miner confirmation window is defined as:
|
||||
// target proof of work timespan / target proof of work spacing
|
||||
RuleChangeActivationThreshold: 1916, // 95% of MinerConfirmationWindow
|
||||
MinerConfirmationWindow: 2016, //
|
||||
Deployments: [DefinedDeployments]ConsensusDeployment{
|
||||
DeploymentTestDummy: {
|
||||
BitNumber: 28,
|
||||
StartTime: 1199145601, // January 1, 2008 UTC
|
||||
ExpireTime: 1230767999, // December 31, 2008 UTC
|
||||
},
|
||||
DeploymentCSV: {
|
||||
BitNumber: 0,
|
||||
StartTime: 1462060800, // May 1st, 2016
|
||||
ExpireTime: 1493596800, // May 1st, 2017
|
||||
},
|
||||
DeploymentSegwit: {
|
||||
BitNumber: 1,
|
||||
StartTime: 1479168000, // November 15, 2016 UTC
|
||||
ExpireTime: 1510704000, // November 15, 2017 UTC.
|
||||
},
|
||||
},
|
||||
|
||||
// Mempool parameters
|
||||
RelayNonStdTxs: false,
|
||||
|
||||
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
||||
// BIP 173.
|
||||
Bech32HRPSegwit: "bc", // always bc for main net
|
||||
|
||||
// Address encoding magics
|
||||
PubKeyHashAddrID: 0x00, // starts with 1
|
||||
ScriptHashAddrID: 0x05, // starts with 3
|
||||
PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed)
|
||||
WitnessPubKeyHashAddrID: 0x06, // starts with p2
|
||||
WitnessScriptHashAddrID: 0x0A, // starts with 7Xh
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
|
||||
HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 0,
|
||||
}
|
||||
|
||||
// RegressionNetParams defines the network parameters for the regression test
|
||||
// Bitcoin network. Not to be confused with the test Bitcoin network (version
|
||||
// 3), this network is sometimes simply called "testnet".
|
||||
var RegressionNetParams = Params{
|
||||
Name: "regtest",
|
||||
Net: wire.TestNet,
|
||||
DefaultPort: "18444",
|
||||
DNSSeeds: []DNSSeed{},
|
||||
|
||||
// Chain parameters
|
||||
GenesisBlock: ®TestGenesisBlock,
|
||||
GenesisHash: ®TestGenesisHash,
|
||||
PowLimit: regressionPowLimit,
|
||||
PowLimitBits: 0x207fffff,
|
||||
CoinbaseMaturity: 100,
|
||||
BIP0034Height: 100000000, // Not active - Permit ver 1 blocks
|
||||
BIP0065Height: 1351, // Used by regression tests
|
||||
BIP0066Height: 1251, // Used by regression tests
|
||||
SubsidyReductionInterval: 150,
|
||||
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
||||
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
||||
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
||||
ReduceMinDifficulty: true,
|
||||
MinDiffReductionTime: time.Minute * 20, // TargetTimePerBlock * 2
|
||||
GenerateSupported: true,
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints: nil,
|
||||
|
||||
// Consensus rule change deployments.
|
||||
//
|
||||
// The miner confirmation window is defined as:
|
||||
// target proof of work timespan / target proof of work spacing
|
||||
RuleChangeActivationThreshold: 108, // 75% of MinerConfirmationWindow
|
||||
MinerConfirmationWindow: 144,
|
||||
Deployments: [DefinedDeployments]ConsensusDeployment{
|
||||
DeploymentTestDummy: {
|
||||
BitNumber: 28,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires
|
||||
},
|
||||
DeploymentCSV: {
|
||||
BitNumber: 0,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires
|
||||
},
|
||||
DeploymentSegwit: {
|
||||
BitNumber: 1,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires.
|
||||
},
|
||||
},
|
||||
|
||||
// Mempool parameters
|
||||
RelayNonStdTxs: true,
|
||||
|
||||
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
||||
// BIP 173.
|
||||
Bech32HRPSegwit: "bcrt", // always bcrt for reg test net
|
||||
|
||||
// Address encoding magics
|
||||
PubKeyHashAddrID: 0x6f, // starts with m or n
|
||||
ScriptHashAddrID: 0xc4, // starts with 2
|
||||
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
|
||||
HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 1,
|
||||
}
|
||||
|
||||
// TestNet3Params defines the network parameters for the test Bitcoin network
|
||||
// (version 3). Not to be confused with the regression test network, this
|
||||
// network is sometimes simply called "testnet".
|
||||
var TestNet3Params = Params{
|
||||
Name: "testnet3",
|
||||
Net: wire.TestNet3,
|
||||
DefaultPort: "18333",
|
||||
DNSSeeds: []DNSSeed{
|
||||
{"testnet-seed.bitcoin.jonasschnelli.ch", true},
|
||||
{"testnet-seed.bitcoin.schildbach.de", false},
|
||||
{"seed.tbtc.petertodd.org", true},
|
||||
{"testnet-seed.bluematt.me", false},
|
||||
},
|
||||
|
||||
// Chain parameters
|
||||
GenesisBlock: &testNet3GenesisBlock,
|
||||
GenesisHash: &testNet3GenesisHash,
|
||||
PowLimit: testNet3PowLimit,
|
||||
PowLimitBits: 0x1d00ffff,
|
||||
BIP0034Height: 21111, // 0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8
|
||||
BIP0065Height: 581885, // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6
|
||||
BIP0066Height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
||||
CoinbaseMaturity: 100,
|
||||
SubsidyReductionInterval: 210000,
|
||||
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
||||
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
||||
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
||||
ReduceMinDifficulty: true,
|
||||
MinDiffReductionTime: time.Minute * 20, // TargetTimePerBlock * 2
|
||||
GenerateSupported: false,
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints: []Checkpoint{
|
||||
{546, newHashFromStr("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")},
|
||||
{100000, newHashFromStr("00000000009e2958c15ff9290d571bf9459e93b19765c6801ddeccadbb160a1e")},
|
||||
{200000, newHashFromStr("0000000000287bffd321963ef05feab753ebe274e1d78b2fd4e2bfe9ad3aa6f2")},
|
||||
{300001, newHashFromStr("0000000000004829474748f3d1bc8fcf893c88be255e6d7f571c548aff57abf4")},
|
||||
{400002, newHashFromStr("0000000005e2c73b8ecb82ae2dbc2e8274614ebad7172b53528aba7501f5a089")},
|
||||
{500011, newHashFromStr("00000000000929f63977fbac92ff570a9bd9e7715401ee96f2848f7b07750b02")},
|
||||
{600002, newHashFromStr("000000000001f471389afd6ee94dcace5ccc44adc18e8bff402443f034b07240")},
|
||||
{700000, newHashFromStr("000000000000406178b12a4dea3b27e13b3c4fe4510994fd667d7c1e6a3f4dc1")},
|
||||
{800010, newHashFromStr("000000000017ed35296433190b6829db01e657d80631d43f5983fa403bfdb4c1")},
|
||||
{900000, newHashFromStr("0000000000356f8d8924556e765b7a94aaebc6b5c8685dcfa2b1ee8b41acd89b")},
|
||||
{1000007, newHashFromStr("00000000001ccb893d8a1f25b70ad173ce955e5f50124261bbbc50379a612ddf")},
|
||||
{1100007, newHashFromStr("00000000000abc7b2cd18768ab3dee20857326a818d1946ed6796f42d66dd1e8")},
|
||||
{1200007, newHashFromStr("00000000000004f2dc41845771909db57e04191714ed8c963f7e56713a7b6cea")},
|
||||
{1300007, newHashFromStr("0000000072eab69d54df75107c052b26b0395b44f77578184293bf1bb1dbd9fa")},
|
||||
},
|
||||
|
||||
// Consensus rule change deployments.
|
||||
//
|
||||
// The miner confirmation window is defined as:
|
||||
// target proof of work timespan / target proof of work spacing
|
||||
RuleChangeActivationThreshold: 1512, // 75% of MinerConfirmationWindow
|
||||
MinerConfirmationWindow: 2016,
|
||||
Deployments: [DefinedDeployments]ConsensusDeployment{
|
||||
DeploymentTestDummy: {
|
||||
BitNumber: 28,
|
||||
StartTime: 1199145601, // January 1, 2008 UTC
|
||||
ExpireTime: 1230767999, // December 31, 2008 UTC
|
||||
},
|
||||
DeploymentCSV: {
|
||||
BitNumber: 0,
|
||||
StartTime: 1456790400, // March 1st, 2016
|
||||
ExpireTime: 1493596800, // May 1st, 2017
|
||||
},
|
||||
DeploymentSegwit: {
|
||||
BitNumber: 1,
|
||||
StartTime: 1462060800, // May 1, 2016 UTC
|
||||
ExpireTime: 1493596800, // May 1, 2017 UTC.
|
||||
},
|
||||
},
|
||||
|
||||
// Mempool parameters
|
||||
RelayNonStdTxs: true,
|
||||
|
||||
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
||||
// BIP 173.
|
||||
Bech32HRPSegwit: "tb", // always tb for test net
|
||||
|
||||
// Address encoding magics
|
||||
PubKeyHashAddrID: 0x6f, // starts with m or n
|
||||
ScriptHashAddrID: 0xc4, // starts with 2
|
||||
WitnessPubKeyHashAddrID: 0x03, // starts with QW
|
||||
WitnessScriptHashAddrID: 0x28, // starts with T7n
|
||||
PrivateKeyID: 0xef, // starts with 9 (uncompressed) or c (compressed)
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDPrivateKeyID: [4]byte{0x04, 0x35, 0x83, 0x94}, // starts with tprv
|
||||
HDPublicKeyID: [4]byte{0x04, 0x35, 0x87, 0xcf}, // starts with tpub
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 1,
|
||||
}
|
||||
|
||||
// SimNetParams defines the network parameters for the simulation test Bitcoin
|
||||
// network. This network is similar to the normal test network except it is
|
||||
// intended for private use within a group of individuals doing simulation
|
||||
// testing. The functionality is intended to differ in that the only nodes
|
||||
// which are specifically specified are used to create the network rather than
|
||||
// following normal discovery rules. This is important as otherwise it would
|
||||
// just turn into another public testnet.
|
||||
var SimNetParams = Params{
|
||||
Name: "simnet",
|
||||
Net: wire.SimNet,
|
||||
DefaultPort: "18555",
|
||||
DNSSeeds: []DNSSeed{}, // NOTE: There must NOT be any seeds.
|
||||
|
||||
// Chain parameters
|
||||
GenesisBlock: &simNetGenesisBlock,
|
||||
GenesisHash: &simNetGenesisHash,
|
||||
PowLimit: simNetPowLimit,
|
||||
PowLimitBits: 0x207fffff,
|
||||
BIP0034Height: 0, // Always active on simnet
|
||||
BIP0065Height: 0, // Always active on simnet
|
||||
BIP0066Height: 0, // Always active on simnet
|
||||
CoinbaseMaturity: 100,
|
||||
SubsidyReductionInterval: 210000,
|
||||
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
||||
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
||||
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
||||
ReduceMinDifficulty: true,
|
||||
MinDiffReductionTime: time.Minute * 20, // TargetTimePerBlock * 2
|
||||
GenerateSupported: true,
|
||||
|
||||
// Checkpoints ordered from oldest to newest.
|
||||
Checkpoints: nil,
|
||||
|
||||
// Consensus rule change deployments.
|
||||
//
|
||||
// The miner confirmation window is defined as:
|
||||
// target proof of work timespan / target proof of work spacing
|
||||
RuleChangeActivationThreshold: 75, // 75% of MinerConfirmationWindow
|
||||
MinerConfirmationWindow: 100,
|
||||
Deployments: [DefinedDeployments]ConsensusDeployment{
|
||||
DeploymentTestDummy: {
|
||||
BitNumber: 28,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires
|
||||
},
|
||||
DeploymentCSV: {
|
||||
BitNumber: 0,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires
|
||||
},
|
||||
DeploymentSegwit: {
|
||||
BitNumber: 1,
|
||||
StartTime: 0, // Always available for vote
|
||||
ExpireTime: math.MaxInt64, // Never expires.
|
||||
},
|
||||
},
|
||||
|
||||
// Mempool parameters
|
||||
RelayNonStdTxs: true,
|
||||
|
||||
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
||||
// BIP 173.
|
||||
Bech32HRPSegwit: "sb", // always sb for sim net
|
||||
|
||||
// Address encoding magics
|
||||
PubKeyHashAddrID: 0x3f, // starts with S
|
||||
ScriptHashAddrID: 0x7b, // starts with s
|
||||
PrivateKeyID: 0x64, // starts with 4 (uncompressed) or F (compressed)
|
||||
WitnessPubKeyHashAddrID: 0x19, // starts with Gg
|
||||
WitnessScriptHashAddrID: 0x28, // starts with ?
|
||||
|
||||
// BIP32 hierarchical deterministic extended key magics
|
||||
HDPrivateKeyID: [4]byte{0x04, 0x20, 0xb9, 0x00}, // starts with sprv
|
||||
HDPublicKeyID: [4]byte{0x04, 0x20, 0xbd, 0x3a}, // starts with spub
|
||||
|
||||
// BIP44 coin type used in the hierarchical deterministic path for
|
||||
// address generation.
|
||||
HDCoinType: 115, // ASCII for s
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrDuplicateNet describes an error where the parameters for a Bitcoin
|
||||
// network could not be set due to the network already being a standard
|
||||
// network or previously-registered into this package.
|
||||
ErrDuplicateNet = errors.New("duplicate Bitcoin network")
|
||||
|
||||
// ErrUnknownHDKeyID describes an error where the provided id which
|
||||
// is intended to identify the network for a hierarchical deterministic
|
||||
// private extended key is not registered.
|
||||
ErrUnknownHDKeyID = errors.New("unknown hd private extended key bytes")
|
||||
)
|
||||
|
||||
var (
|
||||
registeredNets = make(map[wire.BitcoinNet]struct{})
|
||||
pubKeyHashAddrIDs = make(map[byte]struct{})
|
||||
scriptHashAddrIDs = make(map[byte]struct{})
|
||||
bech32SegwitPrefixes = make(map[string]struct{})
|
||||
hdPrivToPubKeyIDs = make(map[[4]byte][]byte)
|
||||
)
|
||||
|
||||
// String returns the hostname of the DNS seed in human-readable form.
|
||||
func (d DNSSeed) String() string {
|
||||
return d.Host
|
||||
}
|
||||
|
||||
// Register registers the network parameters for a Bitcoin network. This may
|
||||
// error with ErrDuplicateNet if the network is already registered (either
|
||||
// due to a previous Register call, or the network being one of the default
|
||||
// networks).
|
||||
//
|
||||
// Network parameters should be registered into this package by a main package
|
||||
// as early as possible. Then, library packages may lookup networks or network
|
||||
// parameters based on inputs and work regardless of the network being standard
|
||||
// or not.
|
||||
func Register(params *Params) error {
|
||||
if _, ok := registeredNets[params.Net]; ok {
|
||||
return ErrDuplicateNet
|
||||
}
|
||||
registeredNets[params.Net] = struct{}{}
|
||||
pubKeyHashAddrIDs[params.PubKeyHashAddrID] = struct{}{}
|
||||
scriptHashAddrIDs[params.ScriptHashAddrID] = struct{}{}
|
||||
hdPrivToPubKeyIDs[params.HDPrivateKeyID] = params.HDPublicKeyID[:]
|
||||
|
||||
// A valid Bech32 encoded segwit address always has as prefix the
|
||||
// human-readable part for the given net followed by '1'.
|
||||
bech32SegwitPrefixes[params.Bech32HRPSegwit+"1"] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustRegister performs the same function as Register except it panics if there
|
||||
// is an error. This should only be called from package init functions.
|
||||
func mustRegister(params *Params) {
|
||||
if err := Register(params); err != nil {
|
||||
panic("failed to register network: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// IsPubKeyHashAddrID returns whether the id is an identifier known to prefix a
|
||||
// pay-to-pubkey-hash address on any default or registered network. This is
|
||||
// used when decoding an address string into a specific address type. It is up
|
||||
// to the caller to check both this and IsScriptHashAddrID and decide whether an
|
||||
// address is a pubkey hash address, script hash address, neither, or
|
||||
// undeterminable (if both return true).
|
||||
func IsPubKeyHashAddrID(id byte) bool {
|
||||
_, ok := pubKeyHashAddrIDs[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsScriptHashAddrID returns whether the id is an identifier known to prefix a
|
||||
// pay-to-script-hash address on any default or registered network. This is
|
||||
// used when decoding an address string into a specific address type. It is up
|
||||
// to the caller to check both this and IsPubKeyHashAddrID and decide whether an
|
||||
// address is a pubkey hash address, script hash address, neither, or
|
||||
// undeterminable (if both return true).
|
||||
func IsScriptHashAddrID(id byte) bool {
|
||||
_, ok := scriptHashAddrIDs[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsBech32SegwitPrefix returns whether the prefix is a known prefix for segwit
|
||||
// addresses on any default or registered network. This is used when decoding
|
||||
// an address string into a specific address type.
|
||||
func IsBech32SegwitPrefix(prefix string) bool {
|
||||
prefix = strings.ToLower(prefix)
|
||||
_, ok := bech32SegwitPrefixes[prefix]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HDPrivateKeyToPublicKeyID accepts a private hierarchical deterministic
|
||||
// extended key id and returns the associated public key id. When the provided
|
||||
// id is not registered, the ErrUnknownHDKeyID error will be returned.
|
||||
func HDPrivateKeyToPublicKeyID(id []byte) ([]byte, error) {
|
||||
if len(id) != 4 {
|
||||
return nil, ErrUnknownHDKeyID
|
||||
}
|
||||
|
||||
var key [4]byte
|
||||
copy(key[:], id)
|
||||
pubBytes, ok := hdPrivToPubKeyIDs[key]
|
||||
if !ok {
|
||||
return nil, ErrUnknownHDKeyID
|
||||
}
|
||||
|
||||
return pubBytes, nil
|
||||
}
|
||||
|
||||
// newHashFromStr converts the passed big-endian hex string into a
|
||||
// chainhash.Hash. It only differs from the one available in chainhash in that
|
||||
// it panics on an error since it will only (and must only) be called with
|
||||
// hard-coded, and therefore known good, hashes.
|
||||
func newHashFromStr(hexStr string) *chainhash.Hash {
|
||||
hash, err := chainhash.NewHashFromStr(hexStr)
|
||||
if err != nil {
|
||||
// Ordinarily I don't like panics in library code since it
|
||||
// can take applications down without them having a chance to
|
||||
// recover which is extremely annoying, however an exception is
|
||||
// being made in this case because the only way this can panic
|
||||
// is if there is an error in the hard-coded hashes. Thus it
|
||||
// will only ever potentially panic on init and therefore is
|
||||
// 100% predictable.
|
||||
panic(err)
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register all default networks when the package is initialized.
|
||||
mustRegister(&MainNetParams)
|
||||
mustRegister(&TestNet3Params)
|
||||
mustRegister(&RegressionNetParams)
|
||||
mustRegister(&SimNetParams)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
connmgr
|
||||
=======
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
|
||||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/connmgr)
|
||||
|
||||
Package connmgr implements a generic Bitcoin network connection manager.
|
||||
|
||||
## Overview
|
||||
|
||||
Connection Manager handles all the general connection concerns such as
|
||||
maintaining a set number of outbound connections, sourcing peers, banning,
|
||||
limiting max connections, tor lookup, etc.
|
||||
|
||||
The package provides a generic connection manager which is able to accept
|
||||
connection requests from a source or a set of given addresses, dial them and
|
||||
notify the caller on connections. The main intended use is to initialize a pool
|
||||
of active connections and maintain them to remain connected to the P2P network.
|
||||
|
||||
In addition the connection manager provides the following utilities:
|
||||
|
||||
- Notifications on connections or disconnections
|
||||
- Handle failures and retry new addresses from the source
|
||||
- Connect only to specified addresses
|
||||
- Permanent connections with increasing backoff retry timers
|
||||
- Disconnect or Remove an established connection
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/connmgr
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Package connmgr is licensed under the [copyfree](http://copyfree.org) ISC License.
|
|
@ -0,0 +1,577 @@
|
|||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// maxFailedAttempts is the maximum number of successive failed connection
|
||||
// attempts after which network failure is assumed and new connections will
|
||||
// be delayed by the configured retry duration.
|
||||
const maxFailedAttempts = 25
|
||||
|
||||
var (
|
||||
//ErrDialNil is used to indicate that Dial cannot be nil in the configuration.
|
||||
ErrDialNil = errors.New("Config: Dial cannot be nil")
|
||||
|
||||
// maxRetryDuration is the max duration of time retrying of a persistent
|
||||
// connection is allowed to grow to. This is necessary since the retry
|
||||
// logic uses a backoff mechanism which increases the interval base times
|
||||
// the number of retries that have been done.
|
||||
maxRetryDuration = time.Minute * 5
|
||||
|
||||
// defaultRetryDuration is the default duration of time for retrying
|
||||
// persistent connections.
|
||||
defaultRetryDuration = time.Second * 5
|
||||
|
||||
// defaultTargetOutbound is the default number of outbound connections to
|
||||
// maintain.
|
||||
defaultTargetOutbound = uint32(8)
|
||||
)
|
||||
|
||||
// ConnState represents the state of the requested connection.
|
||||
type ConnState uint8
|
||||
|
||||
// ConnState can be either pending, established, disconnected or failed. When
|
||||
// a new connection is requested, it is attempted and categorized as
|
||||
// established or failed depending on the connection result. An established
|
||||
// connection which was disconnected is categorized as disconnected.
|
||||
const (
|
||||
ConnPending ConnState = iota
|
||||
ConnFailing
|
||||
ConnCanceled
|
||||
ConnEstablished
|
||||
ConnDisconnected
|
||||
)
|
||||
|
||||
// ConnReq is the connection request to a network address. If permanent, the
|
||||
// connection will be retried on disconnection.
|
||||
type ConnReq struct {
|
||||
// The following variables must only be used atomically.
|
||||
id uint64
|
||||
|
||||
Addr net.Addr
|
||||
Permanent bool
|
||||
|
||||
conn net.Conn
|
||||
state ConnState
|
||||
stateMtx sync.RWMutex
|
||||
retryCount uint32
|
||||
}
|
||||
|
||||
// updateState updates the state of the connection request.
|
||||
func (c *ConnReq) updateState(state ConnState) {
|
||||
c.stateMtx.Lock()
|
||||
c.state = state
|
||||
c.stateMtx.Unlock()
|
||||
}
|
||||
|
||||
// ID returns a unique identifier for the connection request.
|
||||
func (c *ConnReq) ID() uint64 {
|
||||
return atomic.LoadUint64(&c.id)
|
||||
}
|
||||
|
||||
// State is the connection state of the requested connection.
|
||||
func (c *ConnReq) State() ConnState {
|
||||
c.stateMtx.RLock()
|
||||
state := c.state
|
||||
c.stateMtx.RUnlock()
|
||||
return state
|
||||
}
|
||||
|
||||
// String returns a human-readable string for the connection request.
|
||||
func (c *ConnReq) String() string {
|
||||
if c.Addr == nil || c.Addr.String() == "" {
|
||||
return fmt.Sprintf("reqid %d", atomic.LoadUint64(&c.id))
|
||||
}
|
||||
return fmt.Sprintf("%s (reqid %d)", c.Addr, atomic.LoadUint64(&c.id))
|
||||
}
|
||||
|
||||
// Config holds the configuration options related to the connection manager.
|
||||
type Config struct {
|
||||
// Listeners defines a slice of listeners for which the connection
|
||||
// manager will take ownership of and accept connections. When a
|
||||
// connection is accepted, the OnAccept handler will be invoked with the
|
||||
// connection. Since the connection manager takes ownership of these
|
||||
// listeners, they will be closed when the connection manager is
|
||||
// stopped.
|
||||
//
|
||||
// This field will not have any effect if the OnAccept field is not
|
||||
// also specified. It may be nil if the caller does not wish to listen
|
||||
// for incoming connections.
|
||||
Listeners []net.Listener
|
||||
|
||||
// OnAccept is a callback that is fired when an inbound connection is
|
||||
// accepted. It is the caller's responsibility to close the connection.
|
||||
// Failure to close the connection will result in the connection manager
|
||||
// believing the connection is still active and thus have undesirable
|
||||
// side effects such as still counting toward maximum connection limits.
|
||||
//
|
||||
// This field will not have any effect if the Listeners field is not
|
||||
// also specified since there couldn't possibly be any accepted
|
||||
// connections in that case.
|
||||
OnAccept func(net.Conn)
|
||||
|
||||
// TargetOutbound is the number of outbound network connections to
|
||||
// maintain. Defaults to 8.
|
||||
TargetOutbound uint32
|
||||
|
||||
// RetryDuration is the duration to wait before retrying connection
|
||||
// requests. Defaults to 5s.
|
||||
RetryDuration time.Duration
|
||||
|
||||
// OnConnection is a callback that is fired when a new outbound
|
||||
// connection is established.
|
||||
OnConnection func(*ConnReq, net.Conn)
|
||||
|
||||
// OnDisconnection is a callback that is fired when an outbound
|
||||
// connection is disconnected.
|
||||
OnDisconnection func(*ConnReq)
|
||||
|
||||
// GetNewAddress is a way to get an address to make a network connection
|
||||
// to. If nil, no new connections will be made automatically.
|
||||
GetNewAddress func() (net.Addr, error)
|
||||
|
||||
// Dial connects to the address on the named network. It cannot be nil.
|
||||
Dial func(net.Addr) (net.Conn, error)
|
||||
}
|
||||
|
||||
// registerPending is used to register a pending connection attempt. By
|
||||
// registering pending connection attempts we allow callers to cancel pending
|
||||
// connection attempts before their successful or in the case they're not
|
||||
// longer wanted.
|
||||
type registerPending struct {
|
||||
c *ConnReq
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// handleConnected is used to queue a successful connection.
|
||||
type handleConnected struct {
|
||||
c *ConnReq
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
// handleDisconnected is used to remove a connection.
|
||||
type handleDisconnected struct {
|
||||
id uint64
|
||||
retry bool
|
||||
}
|
||||
|
||||
// handleFailed is used to remove a pending connection.
|
||||
type handleFailed struct {
|
||||
c *ConnReq
|
||||
err error
|
||||
}
|
||||
|
||||
// ConnManager provides a manager to handle network connections.
|
||||
type ConnManager struct {
|
||||
// The following variables must only be used atomically.
|
||||
connReqCount uint64
|
||||
start int32
|
||||
stop int32
|
||||
|
||||
cfg Config
|
||||
wg sync.WaitGroup
|
||||
failedAttempts uint64
|
||||
requests chan interface{}
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
// handleFailedConn handles a connection failed due to a disconnect or any
|
||||
// other failure. If permanent, it retries the connection after the configured
|
||||
// retry duration. Otherwise, if required, it makes a new connection request.
|
||||
// After maxFailedConnectionAttempts new connections will be retried after the
|
||||
// configured retry duration.
|
||||
func (cm *ConnManager) handleFailedConn(c *ConnReq) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
if c.Permanent {
|
||||
c.retryCount++
|
||||
d := time.Duration(c.retryCount) * cm.cfg.RetryDuration
|
||||
if d > maxRetryDuration {
|
||||
d = maxRetryDuration
|
||||
}
|
||||
log.Debugf("Retrying connection to %v in %v", c, d)
|
||||
time.AfterFunc(d, func() {
|
||||
cm.Connect(c)
|
||||
})
|
||||
} else if cm.cfg.GetNewAddress != nil {
|
||||
cm.failedAttempts++
|
||||
if cm.failedAttempts >= maxFailedAttempts {
|
||||
log.Debugf("Max failed connection attempts reached: [%d] "+
|
||||
"-- retrying connection in: %v", maxFailedAttempts,
|
||||
cm.cfg.RetryDuration)
|
||||
time.AfterFunc(cm.cfg.RetryDuration, func() {
|
||||
cm.NewConnReq()
|
||||
})
|
||||
} else {
|
||||
go cm.NewConnReq()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connHandler handles all connection related requests. It must be run as a
|
||||
// goroutine.
|
||||
//
|
||||
// The connection handler makes sure that we maintain a pool of active outbound
|
||||
// connections so that we remain connected to the network. Connection requests
|
||||
// are processed and mapped by their assigned ids.
|
||||
func (cm *ConnManager) connHandler() {
|
||||
|
||||
var (
|
||||
// pending holds all registered conn requests that have yet to
|
||||
// succeed.
|
||||
pending = make(map[uint64]*ConnReq)
|
||||
|
||||
// conns represents the set of all actively connected peers.
|
||||
conns = make(map[uint64]*ConnReq, cm.cfg.TargetOutbound)
|
||||
)
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case req := <-cm.requests:
|
||||
switch msg := req.(type) {
|
||||
|
||||
case registerPending:
|
||||
connReq := msg.c
|
||||
connReq.updateState(ConnPending)
|
||||
pending[msg.c.id] = connReq
|
||||
close(msg.done)
|
||||
|
||||
case handleConnected:
|
||||
connReq := msg.c
|
||||
|
||||
if _, ok := pending[connReq.id]; !ok {
|
||||
if msg.conn != nil {
|
||||
msg.conn.Close()
|
||||
}
|
||||
log.Debugf("Ignoring connection for "+
|
||||
"canceled connreq=%v", connReq)
|
||||
continue
|
||||
}
|
||||
|
||||
connReq.updateState(ConnEstablished)
|
||||
connReq.conn = msg.conn
|
||||
conns[connReq.id] = connReq
|
||||
log.Debugf("Connected to %v", connReq)
|
||||
connReq.retryCount = 0
|
||||
cm.failedAttempts = 0
|
||||
|
||||
delete(pending, connReq.id)
|
||||
|
||||
if cm.cfg.OnConnection != nil {
|
||||
go cm.cfg.OnConnection(connReq, msg.conn)
|
||||
}
|
||||
|
||||
case handleDisconnected:
|
||||
connReq, ok := conns[msg.id]
|
||||
if !ok {
|
||||
connReq, ok = pending[msg.id]
|
||||
if !ok {
|
||||
log.Errorf("Unknown connid=%d",
|
||||
msg.id)
|
||||
continue
|
||||
}
|
||||
|
||||
// Pending connection was found, remove
|
||||
// it from pending map if we should
|
||||
// ignore a later, successful
|
||||
// connection.
|
||||
connReq.updateState(ConnCanceled)
|
||||
log.Debugf("Canceling: %v", connReq)
|
||||
delete(pending, msg.id)
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
// An existing connection was located, mark as
|
||||
// disconnected and execute disconnection
|
||||
// callback.
|
||||
log.Debugf("Disconnected from %v", connReq)
|
||||
delete(conns, msg.id)
|
||||
|
||||
if connReq.conn != nil {
|
||||
connReq.conn.Close()
|
||||
}
|
||||
|
||||
if cm.cfg.OnDisconnection != nil {
|
||||
go cm.cfg.OnDisconnection(connReq)
|
||||
}
|
||||
|
||||
// All internal state has been cleaned up, if
|
||||
// this connection is being removed, we will
|
||||
// make no further attempts with this request.
|
||||
if !msg.retry {
|
||||
connReq.updateState(ConnDisconnected)
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, we will attempt a reconnection if
|
||||
// we do not have enough peers, or if this is a
|
||||
// persistent peer. The connection request is
|
||||
// re added to the pending map, so that
|
||||
// subsequent processing of connections and
|
||||
// failures do not ignore the request.
|
||||
if uint32(len(conns)) < cm.cfg.TargetOutbound ||
|
||||
connReq.Permanent {
|
||||
|
||||
connReq.updateState(ConnPending)
|
||||
log.Debugf("Reconnecting to %v",
|
||||
connReq)
|
||||
pending[msg.id] = connReq
|
||||
cm.handleFailedConn(connReq)
|
||||
}
|
||||
|
||||
case handleFailed:
|
||||
connReq := msg.c
|
||||
|
||||
if _, ok := pending[connReq.id]; !ok {
|
||||
log.Debugf("Ignoring connection for "+
|
||||
"canceled conn req: %v", connReq)
|
||||
continue
|
||||
}
|
||||
|
||||
connReq.updateState(ConnFailing)
|
||||
log.Debugf("Failed to connect to %v: %v",
|
||||
connReq, msg.err)
|
||||
cm.handleFailedConn(connReq)
|
||||
}
|
||||
|
||||
case <-cm.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
|
||||
cm.wg.Done()
|
||||
log.Trace("Connection handler done")
|
||||
}
|
||||
|
||||
// NewConnReq creates a new connection request and connects to the
|
||||
// corresponding address.
|
||||
func (cm *ConnManager) NewConnReq() {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
if cm.cfg.GetNewAddress == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := &ConnReq{}
|
||||
atomic.StoreUint64(&c.id, atomic.AddUint64(&cm.connReqCount, 1))
|
||||
|
||||
// Submit a request of a pending connection attempt to the connection
|
||||
// manager. By registering the id before the connection is even
|
||||
// established, we'll be able to later cancel the connection via the
|
||||
// Remove method.
|
||||
done := make(chan struct{})
|
||||
select {
|
||||
case cm.requests <- registerPending{c, done}:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the registration to successfully add the pending conn req to
|
||||
// the conn manager's internal state.
|
||||
select {
|
||||
case <-done:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := cm.cfg.GetNewAddress()
|
||||
if err != nil {
|
||||
select {
|
||||
case cm.requests <- handleFailed{c, err}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.Addr = addr
|
||||
|
||||
cm.Connect(c)
|
||||
}
|
||||
|
||||
// Connect assigns an id and dials a connection to the address of the
|
||||
// connection request.
|
||||
func (cm *ConnManager) Connect(c *ConnReq) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// During the time we wait for retry there is a chance that
|
||||
// this connection was already cancelled
|
||||
if c.State() == ConnCanceled {
|
||||
log.Debugf("Ignoring connect for canceled connreq=%v", c)
|
||||
return
|
||||
}
|
||||
|
||||
if atomic.LoadUint64(&c.id) == 0 {
|
||||
atomic.StoreUint64(&c.id, atomic.AddUint64(&cm.connReqCount, 1))
|
||||
|
||||
// Submit a request of a pending connection attempt to the
|
||||
// connection manager. By registering the id before the
|
||||
// connection is even established, we'll be able to later
|
||||
// cancel the connection via the Remove method.
|
||||
done := make(chan struct{})
|
||||
select {
|
||||
case cm.requests <- registerPending{c, done}:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the registration to successfully add the pending
|
||||
// conn req to the conn manager's internal state.
|
||||
select {
|
||||
case <-done:
|
||||
case <-cm.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Attempting to connect to %v", c)
|
||||
|
||||
conn, err := cm.cfg.Dial(c.Addr)
|
||||
if err != nil {
|
||||
select {
|
||||
case cm.requests <- handleFailed{c, err}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleConnected{c, conn}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect disconnects the connection corresponding to the given connection
|
||||
// id. If permanent, the connection will be retried with an increasing backoff
|
||||
// duration.
|
||||
func (cm *ConnManager) Disconnect(id uint64) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleDisconnected{id, true}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes the connection corresponding to the given connection id from
|
||||
// known connections.
|
||||
//
|
||||
// NOTE: This method can also be used to cancel a lingering connection attempt
|
||||
// that hasn't yet succeeded.
|
||||
func (cm *ConnManager) Remove(id uint64) {
|
||||
if atomic.LoadInt32(&cm.stop) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case cm.requests <- handleDisconnected{id, false}:
|
||||
case <-cm.quit:
|
||||
}
|
||||
}
|
||||
|
||||
// listenHandler accepts incoming connections on a given listener. It must be
|
||||
// run as a goroutine.
|
||||
func (cm *ConnManager) listenHandler(listener net.Listener) {
|
||||
log.Infof("Server listening on %s", listener.Addr())
|
||||
for atomic.LoadInt32(&cm.stop) == 0 {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
// Only log the error if not forcibly shutting down.
|
||||
if atomic.LoadInt32(&cm.stop) == 0 {
|
||||
log.Errorf("Can't accept connection: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
go cm.cfg.OnAccept(conn)
|
||||
}
|
||||
|
||||
cm.wg.Done()
|
||||
log.Tracef("Listener handler done for %s", listener.Addr())
|
||||
}
|
||||
|
||||
// Start launches the connection manager and begins connecting to the network.
|
||||
func (cm *ConnManager) Start() {
|
||||
// Already started?
|
||||
if atomic.AddInt32(&cm.start, 1) != 1 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Trace("Connection manager started")
|
||||
cm.wg.Add(1)
|
||||
go cm.connHandler()
|
||||
|
||||
// Start all the listeners so long as the caller requested them and
|
||||
// provided a callback to be invoked when connections are accepted.
|
||||
if cm.cfg.OnAccept != nil {
|
||||
for _, listner := range cm.cfg.Listeners {
|
||||
cm.wg.Add(1)
|
||||
go cm.listenHandler(listner)
|
||||
}
|
||||
}
|
||||
|
||||
for i := atomic.LoadUint64(&cm.connReqCount); i < uint64(cm.cfg.TargetOutbound); i++ {
|
||||
go cm.NewConnReq()
|
||||
}
|
||||
}
|
||||
|
||||
// Wait blocks until the connection manager halts gracefully.
|
||||
func (cm *ConnManager) Wait() {
|
||||
cm.wg.Wait()
|
||||
}
|
||||
|
||||
// Stop gracefully shuts down the connection manager.
|
||||
func (cm *ConnManager) Stop() {
|
||||
if atomic.AddInt32(&cm.stop, 1) != 1 {
|
||||
log.Warnf("Connection manager already stopped")
|
||||
return
|
||||
}
|
||||
|
||||
// Stop all the listeners. There will not be any listeners if
|
||||
// listening is disabled.
|
||||
for _, listener := range cm.cfg.Listeners {
|
||||
// Ignore the error since this is shutdown and there is no way
|
||||
// to recover anyways.
|
||||
_ = listener.Close()
|
||||
}
|
||||
|
||||
close(cm.quit)
|
||||
log.Trace("Connection manager stopped")
|
||||
}
|
||||
|
||||
// New returns a new connection manager.
|
||||
// Use Start to start connecting to the network.
|
||||
func New(cfg *Config) (*ConnManager, error) {
|
||||
if cfg.Dial == nil {
|
||||
return nil, ErrDialNil
|
||||
}
|
||||
// Default to sane values
|
||||
if cfg.RetryDuration <= 0 {
|
||||
cfg.RetryDuration = defaultRetryDuration
|
||||
}
|
||||
if cfg.TargetOutbound == 0 {
|
||||
cfg.TargetOutbound = defaultTargetOutbound
|
||||
}
|
||||
cm := ConnManager{
|
||||
cfg: *cfg, // Copy so caller can't mutate
|
||||
requests: make(chan interface{}),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
return &cm, nil
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package connmgr implements a generic Bitcoin network connection manager.
|
||||
|
||||
Connection Manager Overview
|
||||
|
||||
Connection Manager handles all the general connection concerns such as
|
||||
maintaining a set number of outbound connections, sourcing peers, banning,
|
||||
limiting max connections, tor lookup, etc.
|
||||
*/
|
||||
package connmgr
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Halflife defines the time (in seconds) by which the transient part
|
||||
// of the ban score decays to one half of it's original value.
|
||||
Halflife = 60
|
||||
|
||||
// lambda is the decaying constant.
|
||||
lambda = math.Ln2 / Halflife
|
||||
|
||||
// Lifetime defines the maximum age of the transient part of the ban
|
||||
// score to be considered a non-zero score (in seconds).
|
||||
Lifetime = 1800
|
||||
|
||||
// precomputedLen defines the amount of decay factors (one per second) that
|
||||
// should be precomputed at initialization.
|
||||
precomputedLen = 64
|
||||
)
|
||||
|
||||
// precomputedFactor stores precomputed exponential decay factors for the first
|
||||
// 'precomputedLen' seconds starting from t == 0.
|
||||
var precomputedFactor [precomputedLen]float64
|
||||
|
||||
// init precomputes decay factors.
|
||||
func init() {
|
||||
for i := range precomputedFactor {
|
||||
precomputedFactor[i] = math.Exp(-1.0 * float64(i) * lambda)
|
||||
}
|
||||
}
|
||||
|
||||
// decayFactor returns the decay factor at t seconds, using precalculated values
|
||||
// if available, or calculating the factor if needed.
|
||||
func decayFactor(t int64) float64 {
|
||||
if t < precomputedLen {
|
||||
return precomputedFactor[t]
|
||||
}
|
||||
return math.Exp(-1.0 * float64(t) * lambda)
|
||||
}
|
||||
|
||||
// DynamicBanScore provides dynamic ban scores consisting of a persistent and a
|
||||
// decaying component. The persistent score could be utilized to create simple
|
||||
// additive banning policies similar to those found in other bitcoin node
|
||||
// implementations.
|
||||
//
|
||||
// The decaying score enables the creation of evasive logic which handles
|
||||
// misbehaving peers (especially application layer DoS attacks) gracefully
|
||||
// by disconnecting and banning peers attempting various kinds of flooding.
|
||||
// DynamicBanScore allows these two approaches to be used in tandem.
|
||||
//
|
||||
// Zero value: Values of type DynamicBanScore are immediately ready for use upon
|
||||
// declaration.
|
||||
type DynamicBanScore struct {
|
||||
lastUnix int64
|
||||
transient float64
|
||||
persistent uint32
|
||||
mtx sync.Mutex
|
||||
}
|
||||
|
||||
// String returns the ban score as a human-readable string.
|
||||
func (s *DynamicBanScore) String() string {
|
||||
s.mtx.Lock()
|
||||
r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
|
||||
s.persistent, s.transient, s.lastUnix, s.Int())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Int returns the current ban score, the sum of the persistent and decaying
|
||||
// scores.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Int() uint32 {
|
||||
s.mtx.Lock()
|
||||
r := s.int(time.Now())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Increase increases both the persistent and decaying scores by the values
|
||||
// passed as parameters. The resulting score is returned.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Increase(persistent, transient uint32) uint32 {
|
||||
s.mtx.Lock()
|
||||
r := s.increase(persistent, transient, time.Now())
|
||||
s.mtx.Unlock()
|
||||
return r
|
||||
}
|
||||
|
||||
// Reset set both persistent and decaying scores to zero.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (s *DynamicBanScore) Reset() {
|
||||
s.mtx.Lock()
|
||||
s.persistent = 0
|
||||
s.transient = 0
|
||||
s.lastUnix = 0
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
// int returns the ban score, the sum of the persistent and decaying scores at a
|
||||
// given point in time.
|
||||
//
|
||||
// This function is not safe for concurrent access. It is intended to be used
|
||||
// internally and during testing.
|
||||
func (s *DynamicBanScore) int(t time.Time) uint32 {
|
||||
dt := t.Unix() - s.lastUnix
|
||||
if s.transient < 1 || dt < 0 || Lifetime < dt {
|
||||
return s.persistent
|
||||
}
|
||||
return s.persistent + uint32(s.transient*decayFactor(dt))
|
||||
}
|
||||
|
||||
// increase increases the persistent, the decaying or both scores by the values
|
||||
// passed as parameters. The resulting score is calculated as if the action was
|
||||
// carried out at the point time represented by the third parameter. The
|
||||
// resulting score is returned.
|
||||
//
|
||||
// This function is not safe for concurrent access.
|
||||
func (s *DynamicBanScore) increase(persistent, transient uint32, t time.Time) uint32 {
|
||||
s.persistent += persistent
|
||||
tu := t.Unix()
|
||||
dt := tu - s.lastUnix
|
||||
|
||||
if transient > 0 {
|
||||
if Lifetime < dt {
|
||||
s.transient = 0
|
||||
} else if s.transient > 1 && dt > 0 {
|
||||
s.transient *= decayFactor(dt)
|
||||
}
|
||||
s.transient += float64(transient)
|
||||
s.lastUnix = tu
|
||||
}
|
||||
return s.persistent + uint32(s.transient)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import "github.com/btcsuite/btclog"
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
DisableLog()
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until either UseLogger or SetLogWriter are called.
|
||||
func DisableLog() {
|
||||
log = btclog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2016 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connmgr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
// These constants are used by the DNS seed code to pick a random last
|
||||
// seen time.
|
||||
secondsIn3Days int32 = 24 * 60 * 60 * 3
|
||||
secondsIn4Days int32 = 24 * 60 * 60 * 4
|
||||
)
|
||||
|
||||
// OnSeed is the signature of the callback function which is invoked when DNS
|
||||
// seeding is succesfull.
|
||||
type OnSeed func(addrs []*wire.NetAddress)
|
||||
|
||||
// LookupFunc is the signature of the DNS lookup function.
|
||||
type LookupFunc func(string) ([]net.IP, error)
|
||||
|
||||
// SeedFromDNS uses DNS seeding to populate the address manager with peers.
|
||||
func SeedFromDNS(chainParams *chaincfg.Params, reqServices wire.ServiceFlag,
|
||||
lookupFn LookupFunc, seedFn OnSeed) {
|
||||
|
||||
for _, dnsseed := range chainParams.DNSSeeds {
|
||||
var host string
|
||||
if !dnsseed.HasFiltering || reqServices == wire.SFNodeNetwork {
|
||||
host = dnsseed.Host
|
||||
} else {
|
||||
host = fmt.Sprintf("x%x.%s", uint64(reqServices), dnsseed.Host)
|
||||
}
|
||||
|
||||
go func(host string) {
|
||||
randSource := mrand.New(mrand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
seedpeers, err := lookupFn(host)
|
||||
if err != nil {
|
||||
log.Infof("DNS discovery failed on seed %s: %v", host, err)
|
||||
return
|
||||
}
|
||||
numPeers := len(seedpeers)
|
||||
|
||||
log.Infof("%d addresses found from DNS seed %s", numPeers, host)
|
||||
|
||||
if numPeers == 0 {
|
||||
return
|
||||
}
|
||||
addresses := make([]*wire.NetAddress, len(seedpeers))
|
||||
// if this errors then we have *real* problems
|
||||
intPort, _ := strconv.Atoi(chainParams.DefaultPort)
|
||||
for i, peer := range seedpeers {
|
||||
addresses[i] = wire.NewNetAddressTimestamp(
|
||||
// bitcoind seeds with addresses from
|
||||
// a time randomly selected between 3
|
||||
// and 7 days ago.
|
||||
time.Now().Add(-1*time.Second*time.Duration(secondsIn3Days+
|
||||
randSource.Int31n(secondsIn4Days))),
|
||||
0, peer, uint16(intPort))
|
||||
}
|
||||
|
||||
seedFn(addresses)
|
||||
}(host)
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue