module: run go mod tidy && go mod vendor
This commit is contained in:
parent
e910ee0475
commit
51614ecd2b
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.12
|
||||||
require (
|
require (
|
||||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d
|
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d
|
||||||
github.com/golang/protobuf v1.2.0
|
github.com/golang/protobuf v1.2.0
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||||
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
github.com/jtolds/gls v4.2.1+incompatible // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.10.0
|
github.com/mattn/go-sqlite3 v1.10.0
|
||||||
github.com/pebbe/zmq4 v1.0.0
|
github.com/pebbe/zmq4 v1.0.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -24,6 +24,8 @@ github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||||
|
|
|
@ -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.
|
|
@ -0,0 +1,68 @@
|
||||||
|
btcec
|
||||||
|
=====
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcec)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](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
|
||||||
|
=======
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcd)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](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,85 @@
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
========
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcd)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](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
|
||||||
|
=========
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcd)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](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,702 @@
|
||||||
|
// 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")},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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")},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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,13 @@
|
||||||
|
# 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>
|
||||||
|
|
||||||
|
Dave Collins <davec@conformal.com>
|
||||||
|
Geert-Johan Riemer <geertjohan.riemer@gmail.com>
|
||||||
|
Josh Rickmar <jrick@conformal.com>
|
||||||
|
Michalis Kargakis <michaliskargakis@gmail.com>
|
||||||
|
Ruben de Vries <ruben@rubensayshi.com
|
|
@ -0,0 +1,56 @@
|
||||||
|
rpcclient
|
||||||
|
=========
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcd)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](http://godoc.org/github.com/btcsuite/btcd/rpcclient)
|
||||||
|
|
||||||
|
rpcclient implements a Websocket-enabled Bitcoin JSON-RPC client package written
|
||||||
|
in [Go](http://golang.org/). It provides a robust and easy to use client for
|
||||||
|
interfacing with a Bitcoin RPC server that uses a btcd/bitcoin core compatible
|
||||||
|
Bitcoin JSON-RPC API.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
This package is currently under active development. It is already stable and
|
||||||
|
the infrastructure is complete. However, there are still several RPCs left to
|
||||||
|
implement and the API is not stable yet.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
* [API Reference](http://godoc.org/github.com/btcsuite/btcd/rpcclient)
|
||||||
|
* [btcd Websockets Example](https://github.com/btcsuite/btcd/tree/master/rpcclient/examples/btcdwebsockets)
|
||||||
|
Connects to a btcd RPC server using TLS-secured websockets, registers for
|
||||||
|
block connected and block disconnected notifications, and gets the current
|
||||||
|
block count
|
||||||
|
* [btcwallet Websockets Example](https://github.com/btcsuite/btcd/tree/master/rpcclient/examples/btcwalletwebsockets)
|
||||||
|
Connects to a btcwallet RPC server using TLS-secured websockets, registers for
|
||||||
|
notifications about changes to account balances, and gets a list of unspent
|
||||||
|
transaction outputs (utxos) the wallet can sign
|
||||||
|
* [Bitcoin Core HTTP POST Example](https://github.com/btcsuite/btcd/tree/master/rpcclient/examples/bitcoincorehttp)
|
||||||
|
Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled
|
||||||
|
and gets the current block count
|
||||||
|
|
||||||
|
## Major Features
|
||||||
|
|
||||||
|
* Supports Websockets (btcd/btcwallet) and HTTP POST mode (bitcoin core)
|
||||||
|
* Provides callback and registration functions for btcd/btcwallet notifications
|
||||||
|
* Supports btcd extensions
|
||||||
|
* Translates to and from higher-level and easier to use Go types
|
||||||
|
* Offers a synchronous (blocking) and asynchronous API
|
||||||
|
* When running in Websockets mode (the default):
|
||||||
|
* Automatic reconnect handling (can be disabled)
|
||||||
|
* Outstanding commands are automatically reissued
|
||||||
|
* Registered notifications are automatically reregistered
|
||||||
|
* Back-off support on reconnect attempts
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go get -u github.com/btcsuite/btcd/rpcclient
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Package rpcclient is licensed under the [copyfree](http://copyfree.org) ISC
|
||||||
|
License.
|
|
@ -0,0 +1,929 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FutureGetBestBlockHashResult is a future promise to deliver the result of a
|
||||||
|
// GetBestBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBestBlockHashResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the hash of
|
||||||
|
// the best block in the longest block chain.
|
||||||
|
func (r FutureGetBestBlockHashResult) Receive() (*chainhash.Hash, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var txHashStr string
|
||||||
|
err = json.Unmarshal(res, &txHashStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return chainhash.NewHashFromStr(txHashStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBestBlockHashAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetBestBlockHash for the blocking version and more details.
|
||||||
|
func (c *Client) GetBestBlockHashAsync() FutureGetBestBlockHashResult {
|
||||||
|
cmd := btcjson.NewGetBestBlockHashCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBestBlockHash returns the hash of the best block in the longest block
|
||||||
|
// chain.
|
||||||
|
func (c *Client) GetBestBlockHash() (*chainhash.Hash, error) {
|
||||||
|
return c.GetBestBlockHashAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the raw
|
||||||
|
// block requested from the server given its hash.
|
||||||
|
func (r FutureGetBlockResult) Receive() (*wire.MsgBlock, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var blockHex string
|
||||||
|
err = json.Unmarshal(res, &blockHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the serialized block hex to raw bytes.
|
||||||
|
serializedBlock, err := hex.DecodeString(blockHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the block and return it.
|
||||||
|
var msgBlock wire.MsgBlock
|
||||||
|
err = msgBlock.Deserialize(bytes.NewReader(serializedBlock))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msgBlock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlock for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlock returns a raw block from the server given its hash.
|
||||||
|
//
|
||||||
|
// See GetBlockVerbose to retrieve a data structure with information about the
|
||||||
|
// block instead.
|
||||||
|
func (c *Client) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
||||||
|
return c.GetBlockAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockVerboseResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockVerboseAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockVerboseResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the data
|
||||||
|
// structure from the server with information about the requested block.
|
||||||
|
func (r FutureGetBlockVerboseResult) Receive() (*btcjson.GetBlockVerboseResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the raw result into a BlockResult.
|
||||||
|
var blockResult btcjson.GetBlockVerboseResult
|
||||||
|
err = json.Unmarshal(res, &blockResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &blockResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockVerboseAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockVerbose for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockVerbose returns a data structure from the server with information
|
||||||
|
// about a block given its hash.
|
||||||
|
//
|
||||||
|
// See GetBlockVerboseTx to retrieve transaction data structures as well.
|
||||||
|
// See GetBlock to retrieve a raw block instead.
|
||||||
|
func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) {
|
||||||
|
return c.GetBlockVerboseAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockVerboseTxAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockVerboseTx or the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), btcjson.Bool(true))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockVerboseTx returns a data structure from the server with information
|
||||||
|
// about a block and its transactions given its hash.
|
||||||
|
//
|
||||||
|
// See GetBlockVerbose if only transaction hashes are preferred.
|
||||||
|
// See GetBlock to retrieve a raw block instead.
|
||||||
|
func (c *Client) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseResult, error) {
|
||||||
|
return c.GetBlockVerboseTxAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockCountResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockCountAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockCountResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the number
|
||||||
|
// of blocks in the longest block chain.
|
||||||
|
func (r FutureGetBlockCountResult) Receive() (int64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as an int64.
|
||||||
|
var count int64
|
||||||
|
err = json.Unmarshal(res, &count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockCountAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockCount for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult {
|
||||||
|
cmd := btcjson.NewGetBlockCountCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockCount returns the number of blocks in the longest block chain.
|
||||||
|
func (c *Client) GetBlockCount() (int64, error) {
|
||||||
|
return c.GetBlockCountAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetDifficultyResult is a future promise to deliver the result of a
|
||||||
|
// GetDifficultyAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetDifficultyResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// proof-of-work difficulty as a multiple of the minimum difficulty.
|
||||||
|
func (r FutureGetDifficultyResult) Receive() (float64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as a float64.
|
||||||
|
var difficulty float64
|
||||||
|
err = json.Unmarshal(res, &difficulty)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return difficulty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDifficultyAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetDifficulty for the blocking version and more details.
|
||||||
|
func (c *Client) GetDifficultyAsync() FutureGetDifficultyResult {
|
||||||
|
cmd := btcjson.NewGetDifficultyCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDifficulty returns the proof-of-work difficulty as a multiple of the
|
||||||
|
// minimum difficulty.
|
||||||
|
func (c *Client) GetDifficulty() (float64, error) {
|
||||||
|
return c.GetDifficultyAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockChainInfoResult is a promise to deliver the result of a
|
||||||
|
// GetBlockChainInfoAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockChainInfoResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns chain info
|
||||||
|
// result provided by the server.
|
||||||
|
func (r FutureGetBlockChainInfoResult) Receive() (*btcjson.GetBlockChainInfoResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var chainInfo btcjson.GetBlockChainInfoResult
|
||||||
|
if err := json.Unmarshal(res, &chainInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &chainInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockChainInfoAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function
|
||||||
|
// on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockChainInfo for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockChainInfoAsync() FutureGetBlockChainInfoResult {
|
||||||
|
cmd := btcjson.NewGetBlockChainInfoCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockChainInfo returns information related to the processing state of
|
||||||
|
// various chain-specific details such as the current difficulty from the tip
|
||||||
|
// of the main chain.
|
||||||
|
func (c *Client) GetBlockChainInfo() (*btcjson.GetBlockChainInfoResult, error) {
|
||||||
|
return c.GetBlockChainInfoAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockHashResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockHashAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockHashResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the hash of
|
||||||
|
// the block in the best block chain at the given height.
|
||||||
|
func (r FutureGetBlockHashResult) Receive() (*chainhash.Hash, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as a string-encoded sha.
|
||||||
|
var txHashStr string
|
||||||
|
err = json.Unmarshal(res, &txHashStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return chainhash.NewHashFromStr(txHashStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHashAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockHash for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockHashAsync(blockHeight int64) FutureGetBlockHashResult {
|
||||||
|
cmd := btcjson.NewGetBlockHashCmd(blockHeight)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHash returns the hash of the block in the best block chain at the
|
||||||
|
// given height.
|
||||||
|
func (c *Client) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
||||||
|
return c.GetBlockHashAsync(blockHeight).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockHeaderResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockHeaderAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockHeaderResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// blockheader requested from the server given its hash.
|
||||||
|
func (r FutureGetBlockHeaderResult) Receive() (*wire.BlockHeader, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var bhHex string
|
||||||
|
err = json.Unmarshal(res, &bhHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedBH, err := hex.DecodeString(bhHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the blockheader and return it.
|
||||||
|
var bh wire.BlockHeader
|
||||||
|
err = bh.Deserialize(bytes.NewReader(serializedBH))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bh, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHeaderAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockHeader for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockHeaderAsync(blockHash *chainhash.Hash) FutureGetBlockHeaderResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetBlockHeaderCmd(hash, btcjson.Bool(false))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHeader returns the blockheader from the server given its hash.
|
||||||
|
//
|
||||||
|
// See GetBlockHeaderVerbose to retrieve a data structure with information about the
|
||||||
|
// block instead.
|
||||||
|
func (c *Client) GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, error) {
|
||||||
|
return c.GetBlockHeaderAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBlockHeaderVerboseResult is a future promise to deliver the result of a
|
||||||
|
// GetBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBlockHeaderVerboseResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// data structure of the blockheader requested from the server given its hash.
|
||||||
|
func (r FutureGetBlockHeaderVerboseResult) Receive() (*btcjson.GetBlockHeaderVerboseResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var bh btcjson.GetBlockHeaderVerboseResult
|
||||||
|
err = json.Unmarshal(res, &bh)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &bh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHeaderVerboseAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBlockHeader for the blocking version and more details.
|
||||||
|
func (c *Client) GetBlockHeaderVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockHeaderVerboseResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetBlockHeaderCmd(hash, btcjson.Bool(true))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBlockHeaderVerbose returns a data structure with information about the
|
||||||
|
// blockheader from the server given its hash.
|
||||||
|
//
|
||||||
|
// See GetBlockHeader to retrieve a blockheader instead.
|
||||||
|
func (c *Client) GetBlockHeaderVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockHeaderVerboseResult, error) {
|
||||||
|
return c.GetBlockHeaderVerboseAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetMempoolEntryResult is a future promise to deliver the result of a
|
||||||
|
// GetMempoolEntryAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetMempoolEntryResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a data
|
||||||
|
// structure with information about the transaction in the memory pool given
|
||||||
|
// its hash.
|
||||||
|
func (r FutureGetMempoolEntryResult) Receive() (*btcjson.GetMempoolEntryResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as an array of strings.
|
||||||
|
var mempoolEntryResult btcjson.GetMempoolEntryResult
|
||||||
|
err = json.Unmarshal(res, &mempoolEntryResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &mempoolEntryResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMempoolEntryAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetMempoolEntry for the blocking version and more details.
|
||||||
|
func (c *Client) GetMempoolEntryAsync(txHash string) FutureGetMempoolEntryResult {
|
||||||
|
cmd := btcjson.NewGetMempoolEntryCmd(txHash)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMempoolEntry returns a data structure with information about the
|
||||||
|
// transaction in the memory pool given its hash.
|
||||||
|
func (c *Client) GetMempoolEntry(txHash string) (*btcjson.GetMempoolEntryResult, error) {
|
||||||
|
return c.GetMempoolEntryAsync(txHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetRawMempoolResult is a future promise to deliver the result of a
|
||||||
|
// GetRawMempoolAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetRawMempoolResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the hashes
|
||||||
|
// of all transactions in the memory pool.
|
||||||
|
func (r FutureGetRawMempoolResult) Receive() ([]*chainhash.Hash, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as an array of strings.
|
||||||
|
var txHashStrs []string
|
||||||
|
err = json.Unmarshal(res, &txHashStrs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a slice of ShaHash arrays from the string slice.
|
||||||
|
txHashes := make([]*chainhash.Hash, 0, len(txHashStrs))
|
||||||
|
for _, hashStr := range txHashStrs {
|
||||||
|
txHash, err := chainhash.NewHashFromStr(hashStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
txHashes = append(txHashes, txHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return txHashes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawMempoolAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetRawMempool for the blocking version and more details.
|
||||||
|
func (c *Client) GetRawMempoolAsync() FutureGetRawMempoolResult {
|
||||||
|
cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(false))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawMempool returns the hashes of all transactions in the memory pool.
|
||||||
|
//
|
||||||
|
// See GetRawMempoolVerbose to retrieve data structures with information about
|
||||||
|
// the transactions instead.
|
||||||
|
func (c *Client) GetRawMempool() ([]*chainhash.Hash, error) {
|
||||||
|
return c.GetRawMempoolAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetRawMempoolVerboseResult is a future promise to deliver the result of
|
||||||
|
// a GetRawMempoolVerboseAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetRawMempoolVerboseResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a map of
|
||||||
|
// transaction hashes to an associated data structure with information about the
|
||||||
|
// transaction for all transactions in the memory pool.
|
||||||
|
func (r FutureGetRawMempoolVerboseResult) Receive() (map[string]btcjson.GetRawMempoolVerboseResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as a map of strings (tx shas) to their detailed
|
||||||
|
// results.
|
||||||
|
var mempoolItems map[string]btcjson.GetRawMempoolVerboseResult
|
||||||
|
err = json.Unmarshal(res, &mempoolItems)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mempoolItems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawMempoolVerboseAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetRawMempoolVerbose for the blocking version and more details.
|
||||||
|
func (c *Client) GetRawMempoolVerboseAsync() FutureGetRawMempoolVerboseResult {
|
||||||
|
cmd := btcjson.NewGetRawMempoolCmd(btcjson.Bool(true))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawMempoolVerbose returns a map of transaction hashes to an associated
|
||||||
|
// data structure with information about the transaction for all transactions in
|
||||||
|
// the memory pool.
|
||||||
|
//
|
||||||
|
// See GetRawMempool to retrieve only the transaction hashes instead.
|
||||||
|
func (c *Client) GetRawMempoolVerbose() (map[string]btcjson.GetRawMempoolVerboseResult, error) {
|
||||||
|
return c.GetRawMempoolVerboseAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureEstimateFeeResult is a future promise to deliver the result of a
|
||||||
|
// EstimateFeeAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureEstimateFeeResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the info
|
||||||
|
// provided by the server.
|
||||||
|
func (r FutureEstimateFeeResult) Receive() (float64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getinfo result object.
|
||||||
|
var fee float64
|
||||||
|
err = json.Unmarshal(res, &fee)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fee, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateFeeAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See EstimateFee for the blocking version and more details.
|
||||||
|
func (c *Client) EstimateFeeAsync(numBlocks int64) FutureEstimateFeeResult {
|
||||||
|
cmd := btcjson.NewEstimateFeeCmd(numBlocks)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateFee provides an estimated fee in bitcoins per kilobyte.
|
||||||
|
func (c *Client) EstimateFee(numBlocks int64) (float64, error) {
|
||||||
|
return c.EstimateFeeAsync(numBlocks).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureVerifyChainResult is a future promise to deliver the result of a
|
||||||
|
// VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync
|
||||||
|
// invocation (or an applicable error).
|
||||||
|
type FutureVerifyChainResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns whether
|
||||||
|
// or not the chain verified based on the check level and number of blocks
|
||||||
|
// to verify specified in the original call.
|
||||||
|
func (r FutureVerifyChainResult) Receive() (bool, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as a boolean.
|
||||||
|
var verified bool
|
||||||
|
err = json.Unmarshal(res, &verified)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return verified, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChainAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See VerifyChain for the blocking version and more details.
|
||||||
|
func (c *Client) VerifyChainAsync() FutureVerifyChainResult {
|
||||||
|
cmd := btcjson.NewVerifyChainCmd(nil, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChain requests the server to verify the block chain database using
|
||||||
|
// the default check level and number of blocks to verify.
|
||||||
|
//
|
||||||
|
// See VerifyChainLevel and VerifyChainBlocks to override the defaults.
|
||||||
|
func (c *Client) VerifyChain() (bool, error) {
|
||||||
|
return c.VerifyChainAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChainLevelAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See VerifyChainLevel for the blocking version and more details.
|
||||||
|
func (c *Client) VerifyChainLevelAsync(checkLevel int32) FutureVerifyChainResult {
|
||||||
|
cmd := btcjson.NewVerifyChainCmd(&checkLevel, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChainLevel requests the server to verify the block chain database using
|
||||||
|
// the passed check level and default number of blocks to verify.
|
||||||
|
//
|
||||||
|
// The check level controls how thorough the verification is with higher numbers
|
||||||
|
// increasing the amount of checks done as consequently how long the
|
||||||
|
// verification takes.
|
||||||
|
//
|
||||||
|
// See VerifyChain to use the default check level and VerifyChainBlocks to
|
||||||
|
// override the number of blocks to verify.
|
||||||
|
func (c *Client) VerifyChainLevel(checkLevel int32) (bool, error) {
|
||||||
|
return c.VerifyChainLevelAsync(checkLevel).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChainBlocksAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See VerifyChainBlocks for the blocking version and more details.
|
||||||
|
func (c *Client) VerifyChainBlocksAsync(checkLevel, numBlocks int32) FutureVerifyChainResult {
|
||||||
|
cmd := btcjson.NewVerifyChainCmd(&checkLevel, &numBlocks)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyChainBlocks requests the server to verify the block chain database
|
||||||
|
// using the passed check level and number of blocks to verify.
|
||||||
|
//
|
||||||
|
// The check level controls how thorough the verification is with higher numbers
|
||||||
|
// increasing the amount of checks done as consequently how long the
|
||||||
|
// verification takes.
|
||||||
|
//
|
||||||
|
// The number of blocks refers to the number of blocks from the end of the
|
||||||
|
// current longest chain.
|
||||||
|
//
|
||||||
|
// See VerifyChain and VerifyChainLevel to use defaults.
|
||||||
|
func (c *Client) VerifyChainBlocks(checkLevel, numBlocks int32) (bool, error) {
|
||||||
|
return c.VerifyChainBlocksAsync(checkLevel, numBlocks).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetTxOutResult is a future promise to deliver the result of a
|
||||||
|
// GetTxOutAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetTxOutResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a
|
||||||
|
// transaction given its hash.
|
||||||
|
func (r FutureGetTxOutResult) Receive() (*btcjson.GetTxOutResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of the special case where the output has been spent already
|
||||||
|
// it should return the string "null"
|
||||||
|
if string(res) == "null" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an gettxout result object.
|
||||||
|
var txOutInfo *btcjson.GetTxOutResult
|
||||||
|
err = json.Unmarshal(res, &txOutInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return txOutInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTxOutAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetTxOut for the blocking version and more details.
|
||||||
|
func (c *Client) GetTxOutAsync(txHash *chainhash.Hash, index uint32, mempool bool) FutureGetTxOutResult {
|
||||||
|
hash := ""
|
||||||
|
if txHash != nil {
|
||||||
|
hash = txHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetTxOutCmd(hash, index, &mempool)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTxOut returns the transaction output info if it's unspent and
|
||||||
|
// nil, otherwise.
|
||||||
|
func (c *Client) GetTxOut(txHash *chainhash.Hash, index uint32, mempool bool) (*btcjson.GetTxOutResult, error) {
|
||||||
|
return c.GetTxOutAsync(txHash, index, mempool).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureRescanBlocksResult is a future promise to deliver the result of a
|
||||||
|
// RescanBlocksAsync RPC invocation (or an applicable error).
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
type FutureRescanBlocksResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// discovered rescanblocks data.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (r FutureRescanBlocksResult) Receive() ([]btcjson.RescannedBlock, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rescanBlocksResult []btcjson.RescannedBlock
|
||||||
|
err = json.Unmarshal(res, &rescanBlocksResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rescanBlocksResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RescanBlocksAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See RescanBlocks for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) RescanBlocksAsync(blockHashes []chainhash.Hash) FutureRescanBlocksResult {
|
||||||
|
strBlockHashes := make([]string, len(blockHashes))
|
||||||
|
for i := range blockHashes {
|
||||||
|
strBlockHashes[i] = blockHashes[i].String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewRescanBlocksCmd(strBlockHashes)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RescanBlocks rescans the blocks identified by blockHashes, in order, using
|
||||||
|
// the client's loaded transaction filter. The blocks do not need to be on the
|
||||||
|
// main chain, but they do need to be adjacent to each other.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) RescanBlocks(blockHashes []chainhash.Hash) ([]btcjson.RescannedBlock, error) {
|
||||||
|
return c.RescanBlocksAsync(blockHashes).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureInvalidateBlockResult is a future promise to deliver the result of a
|
||||||
|
// InvalidateBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureInvalidateBlockResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the raw
|
||||||
|
// block requested from the server given its hash.
|
||||||
|
func (r FutureInvalidateBlockResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidateBlockAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See InvalidateBlock for the blocking version and more details.
|
||||||
|
func (c *Client) InvalidateBlockAsync(blockHash *chainhash.Hash) FutureInvalidateBlockResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewInvalidateBlockCmd(hash)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidateBlock invalidates a specific block.
|
||||||
|
func (c *Client) InvalidateBlock(blockHash *chainhash.Hash) error {
|
||||||
|
return c.InvalidateBlockAsync(blockHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetCFilterResult is a future promise to deliver the result of a
|
||||||
|
// GetCFilterAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetCFilterResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the raw
|
||||||
|
// filter requested from the server given its block hash.
|
||||||
|
func (r FutureGetCFilterResult) Receive() (*wire.MsgCFilter, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var filterHex string
|
||||||
|
err = json.Unmarshal(res, &filterHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the serialized cf hex to raw bytes.
|
||||||
|
serializedFilter, err := hex.DecodeString(filterHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the filter bytes to the correct field of the wire message.
|
||||||
|
// We aren't going to set the block hash or extended flag, since we
|
||||||
|
// don't actually get that back in the RPC response.
|
||||||
|
var msgCFilter wire.MsgCFilter
|
||||||
|
msgCFilter.Data = serializedFilter
|
||||||
|
return &msgCFilter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCFilterAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetCFilter for the blocking version and more details.
|
||||||
|
func (c *Client) GetCFilterAsync(blockHash *chainhash.Hash,
|
||||||
|
filterType wire.FilterType) FutureGetCFilterResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetCFilterCmd(hash, filterType)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCFilter returns a raw filter from the server given its block hash.
|
||||||
|
func (c *Client) GetCFilter(blockHash *chainhash.Hash,
|
||||||
|
filterType wire.FilterType) (*wire.MsgCFilter, error) {
|
||||||
|
return c.GetCFilterAsync(blockHash, filterType).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetCFilterHeaderResult is a future promise to deliver the result of a
|
||||||
|
// GetCFilterHeaderAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetCFilterHeaderResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the raw
|
||||||
|
// filter header requested from the server given its block hash.
|
||||||
|
func (r FutureGetCFilterHeaderResult) Receive() (*wire.MsgCFHeaders, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var headerHex string
|
||||||
|
err = json.Unmarshal(res, &headerHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the decoded header into a hash
|
||||||
|
headerHash, err := chainhash.NewHashFromStr(headerHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the hash to a headers message and return it.
|
||||||
|
msgCFHeaders := wire.MsgCFHeaders{PrevFilterHeader: *headerHash}
|
||||||
|
return &msgCFHeaders, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCFilterHeaderAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function
|
||||||
|
// on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetCFilterHeader for the blocking version and more details.
|
||||||
|
func (c *Client) GetCFilterHeaderAsync(blockHash *chainhash.Hash,
|
||||||
|
filterType wire.FilterType) FutureGetCFilterHeaderResult {
|
||||||
|
hash := ""
|
||||||
|
if blockHash != nil {
|
||||||
|
hash = blockHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetCFilterHeaderCmd(hash, filterType)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCFilterHeader returns a raw filter header from the server given its block
|
||||||
|
// hash.
|
||||||
|
func (c *Client) GetCFilterHeader(blockHash *chainhash.Hash,
|
||||||
|
filterType wire.FilterType) (*wire.MsgCFHeaders, error) {
|
||||||
|
return c.GetCFilterHeaderAsync(blockHash, filterType).Receive()
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
// 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 rpcclient implements a websocket-enabled Bitcoin JSON-RPC client.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
|
||||||
|
This client provides a robust and easy to use client for interfacing with a
|
||||||
|
Bitcoin RPC server that uses a btcd/bitcoin core compatible Bitcoin JSON-RPC
|
||||||
|
API. This client has been tested with btcd (https://github.com/btcsuite/btcd),
|
||||||
|
btcwallet (https://github.com/btcsuite/btcwallet), and
|
||||||
|
bitcoin core (https://github.com/bitcoin).
|
||||||
|
|
||||||
|
In addition to the compatible standard HTTP POST JSON-RPC API, btcd and
|
||||||
|
btcwallet provide a websocket interface that is more efficient than the standard
|
||||||
|
HTTP POST method of accessing RPC. The section below discusses the differences
|
||||||
|
between HTTP POST and websockets.
|
||||||
|
|
||||||
|
By default, this client assumes the RPC server supports websockets and has
|
||||||
|
TLS enabled. In practice, this currently means it assumes you are talking to
|
||||||
|
btcd or btcwallet by default. However, configuration options are provided to
|
||||||
|
fall back to HTTP POST and disable TLS to support talking with inferior bitcoin
|
||||||
|
core style RPC servers.
|
||||||
|
|
||||||
|
Websockets vs HTTP POST
|
||||||
|
|
||||||
|
In HTTP POST-based JSON-RPC, every request creates a new HTTP connection,
|
||||||
|
issues the call, waits for the response, and closes the connection. This adds
|
||||||
|
quite a bit of overhead to every call and lacks flexibility for features such as
|
||||||
|
notifications.
|
||||||
|
|
||||||
|
In contrast, the websocket-based JSON-RPC interface provided by btcd and
|
||||||
|
btcwallet only uses a single connection that remains open and allows
|
||||||
|
asynchronous bi-directional communication.
|
||||||
|
|
||||||
|
The websocket interface supports all of the same commands as HTTP POST, but they
|
||||||
|
can be invoked without having to go through a connect/disconnect cycle for every
|
||||||
|
call. In addition, the websocket interface provides other nice features such as
|
||||||
|
the ability to register for asynchronous notifications of various events.
|
||||||
|
|
||||||
|
Synchronous vs Asynchronous API
|
||||||
|
|
||||||
|
The client provides both a synchronous (blocking) and asynchronous API.
|
||||||
|
|
||||||
|
The synchronous (blocking) API is typically sufficient for most use cases. It
|
||||||
|
works by issuing the RPC and blocking until the response is received. This
|
||||||
|
allows straightforward code where you have the response as soon as the function
|
||||||
|
returns.
|
||||||
|
|
||||||
|
The asynchronous API works on the concept of futures. When you invoke the async
|
||||||
|
version of a command, it will quickly return an instance of a type that promises
|
||||||
|
to provide the result of the RPC at some future time. In the background, the
|
||||||
|
RPC call is issued and the result is stored in the returned instance. Invoking
|
||||||
|
the Receive method on the returned instance will either return the result
|
||||||
|
immediately if it has already arrived, or block until it has. This is useful
|
||||||
|
since it provides the caller with greater control over concurrency.
|
||||||
|
|
||||||
|
Notifications
|
||||||
|
|
||||||
|
The first important part of notifications is to realize that they will only
|
||||||
|
work when connected via websockets. This should intuitively make sense
|
||||||
|
because HTTP POST mode does not keep a connection open!
|
||||||
|
|
||||||
|
All notifications provided by btcd require registration to opt-in. For example,
|
||||||
|
if you want to be notified when funds are received by a set of addresses, you
|
||||||
|
register the addresses via the NotifyReceived (or NotifyReceivedAsync) function.
|
||||||
|
|
||||||
|
Notification Handlers
|
||||||
|
|
||||||
|
Notifications are exposed by the client through the use of callback handlers
|
||||||
|
which are setup via a NotificationHandlers instance that is specified by the
|
||||||
|
caller when creating the client.
|
||||||
|
|
||||||
|
It is important that these notification handlers complete quickly since they
|
||||||
|
are intentionally in the main read loop and will block further reads until
|
||||||
|
they complete. This provides the caller with the flexibility to decide what to
|
||||||
|
do when notifications are coming in faster than they are being handled.
|
||||||
|
|
||||||
|
In particular this means issuing a blocking RPC call from a callback handler
|
||||||
|
will cause a deadlock as more server responses won't be read until the callback
|
||||||
|
returns, but the callback would be waiting for a response. Thus, any
|
||||||
|
additional RPCs must be issued an a completely decoupled manner.
|
||||||
|
|
||||||
|
Automatic Reconnection
|
||||||
|
|
||||||
|
By default, when running in websockets mode, this client will automatically
|
||||||
|
keep trying to reconnect to the RPC server should the connection be lost. There
|
||||||
|
is a back-off in between each connection attempt until it reaches one try per
|
||||||
|
minute. Once a connection is re-established, all previously registered
|
||||||
|
notifications are automatically re-registered and any in-flight commands are
|
||||||
|
re-issued. This means from the caller's perspective, the request simply takes
|
||||||
|
longer to complete.
|
||||||
|
|
||||||
|
The caller may invoke the Shutdown method on the client to force the client
|
||||||
|
to cease reconnect attempts and return ErrClientShutdown for all outstanding
|
||||||
|
commands.
|
||||||
|
|
||||||
|
The automatic reconnection can be disabled by setting the DisableAutoReconnect
|
||||||
|
flag to true in the connection config when creating the client.
|
||||||
|
|
||||||
|
Minor RPC Server Differences and Chain/Wallet Separation
|
||||||
|
|
||||||
|
Some of the commands are extensions specific to a particular RPC server. For
|
||||||
|
example, the DebugLevel call is an extension only provided by btcd (and
|
||||||
|
btcwallet passthrough). Therefore if you call one of these commands against
|
||||||
|
an RPC server that doesn't provide them, you will get an unimplemented error
|
||||||
|
from the server. An effort has been made to call out which commmands are
|
||||||
|
extensions in their documentation.
|
||||||
|
|
||||||
|
Also, it is important to realize that btcd intentionally separates the wallet
|
||||||
|
functionality into a separate process named btcwallet. This means if you are
|
||||||
|
connected to the btcd RPC server directly, only the RPCs which are related to
|
||||||
|
chain services will be available. Depending on your application, you might only
|
||||||
|
need chain-related RPCs. In contrast, btcwallet provides pass through treatment
|
||||||
|
for chain-related RPCs, so it supports them in addition to wallet-related RPCs.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
There are 3 categories of errors that will be returned throughout this package:
|
||||||
|
|
||||||
|
- Errors related to the client connection such as authentication, endpoint,
|
||||||
|
disconnect, and shutdown
|
||||||
|
- Errors that occur before communicating with the remote RPC server such as
|
||||||
|
command creation and marshaling errors or issues talking to the remote
|
||||||
|
server
|
||||||
|
- Errors returned from the remote RPC server like unimplemented commands,
|
||||||
|
nonexistent requested blocks and transactions, malformed data, and incorrect
|
||||||
|
networks
|
||||||
|
|
||||||
|
The first category of errors are typically one of ErrInvalidAuth,
|
||||||
|
ErrInvalidEndpoint, ErrClientDisconnect, or ErrClientShutdown.
|
||||||
|
|
||||||
|
NOTE: The ErrClientDisconnect will not be returned unless the
|
||||||
|
DisableAutoReconnect flag is set since the client automatically handles
|
||||||
|
reconnect by default as previously described.
|
||||||
|
|
||||||
|
The second category of errors typically indicates a programmer error and as such
|
||||||
|
the type can vary, but usually will be best handled by simply showing/logging
|
||||||
|
it.
|
||||||
|
|
||||||
|
The third category of errors, that is errors returned by the server, can be
|
||||||
|
detected by type asserting the error in a *btcjson.RPCError. For example, to
|
||||||
|
detect if a command is unimplemented by the remote RPC server:
|
||||||
|
|
||||||
|
amount, err := client.GetBalance("")
|
||||||
|
if err != nil {
|
||||||
|
if jerr, ok := err.(*btcjson.RPCError); ok {
|
||||||
|
switch jerr.Code {
|
||||||
|
case btcjson.ErrRPCUnimplemented:
|
||||||
|
// Handle not implemented error
|
||||||
|
|
||||||
|
// Handle other specific errors you care about
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log or otherwise handle the error knowing it was not one returned
|
||||||
|
// from the remote RPC server.
|
||||||
|
}
|
||||||
|
|
||||||
|
Example Usage
|
||||||
|
|
||||||
|
The following full-blown client examples are in the examples directory:
|
||||||
|
|
||||||
|
- bitcoincorehttp
|
||||||
|
Connects to a bitcoin core RPC server using HTTP POST mode with TLS disabled
|
||||||
|
and gets the current block count
|
||||||
|
- btcdwebsockets
|
||||||
|
Connects to a btcd RPC server using TLS-secured websockets, registers for
|
||||||
|
block connected and block disconnected notifications, and gets the current
|
||||||
|
block count
|
||||||
|
- btcwalletwebsockets
|
||||||
|
Connects to a btcwallet RPC server using TLS-secured websockets, registers
|
||||||
|
for notifications about changes to account balances, and gets a list of
|
||||||
|
unspent transaction outputs (utxos) the wallet can sign
|
||||||
|
*/
|
||||||
|
package rpcclient
|
|
@ -0,0 +1,473 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FutureDebugLevelResult is a future promise to deliver the result of a
|
||||||
|
// DebugLevelAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureDebugLevelResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the result
|
||||||
|
// of setting the debug logging level to the passed level specification or the
|
||||||
|
// list of of the available subsystems for the special keyword 'show'.
|
||||||
|
func (r FutureDebugLevelResult) Receive() (string, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmashal the result as a string.
|
||||||
|
var result string
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugLevelAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See DebugLevel for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) DebugLevelAsync(levelSpec string) FutureDebugLevelResult {
|
||||||
|
cmd := btcjson.NewDebugLevelCmd(levelSpec)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugLevel dynamically sets the debug logging level to the passed level
|
||||||
|
// specification.
|
||||||
|
//
|
||||||
|
// The levelspec can be either a debug level or of the form:
|
||||||
|
// <subsystem>=<level>,<subsystem2>=<level2>,...
|
||||||
|
//
|
||||||
|
// Additionally, the special keyword 'show' can be used to get a list of the
|
||||||
|
// available subsystems.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) DebugLevel(levelSpec string) (string, error) {
|
||||||
|
return c.DebugLevelAsync(levelSpec).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureCreateEncryptedWalletResult is a future promise to deliver the error
|
||||||
|
// result of a CreateEncryptedWalletAsync RPC invocation.
|
||||||
|
type FutureCreateEncryptedWalletResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for and returns the error response promised by the future.
|
||||||
|
func (r FutureCreateEncryptedWalletResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEncryptedWalletAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See CreateEncryptedWallet for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcwallet extension.
|
||||||
|
func (c *Client) CreateEncryptedWalletAsync(passphrase string) FutureCreateEncryptedWalletResult {
|
||||||
|
cmd := btcjson.NewCreateEncryptedWalletCmd(passphrase)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateEncryptedWallet requests the creation of an encrypted wallet. Wallets
|
||||||
|
// managed by btcwallet are only written to disk with encrypted private keys,
|
||||||
|
// and generating wallets on the fly is impossible as it requires user input for
|
||||||
|
// the encryption passphrase. This RPC specifies the passphrase and instructs
|
||||||
|
// the wallet creation. This may error if a wallet is already opened, or the
|
||||||
|
// new wallet cannot be written to disk.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcwallet extension.
|
||||||
|
func (c *Client) CreateEncryptedWallet(passphrase string) error {
|
||||||
|
return c.CreateEncryptedWalletAsync(passphrase).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureListAddressTransactionsResult is a future promise to deliver the result
|
||||||
|
// of a ListAddressTransactionsAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureListAddressTransactionsResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about all transactions associated with the provided addresses.
|
||||||
|
func (r FutureListAddressTransactionsResult) Receive() ([]btcjson.ListTransactionsResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal the result as an array of listtransactions objects.
|
||||||
|
var transactions []btcjson.ListTransactionsResult
|
||||||
|
err = json.Unmarshal(res, &transactions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return transactions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAddressTransactionsAsync returns an instance of a type that can be used
|
||||||
|
// to get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See ListAddressTransactions for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) ListAddressTransactionsAsync(addresses []btcutil.Address, account string) FutureListAddressTransactionsResult {
|
||||||
|
// Convert addresses to strings.
|
||||||
|
addrs := make([]string, 0, len(addresses))
|
||||||
|
for _, addr := range addresses {
|
||||||
|
addrs = append(addrs, addr.EncodeAddress())
|
||||||
|
}
|
||||||
|
cmd := btcjson.NewListAddressTransactionsCmd(addrs, &account)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAddressTransactions returns information about all transactions associated
|
||||||
|
// with the provided addresses.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcwallet extension.
|
||||||
|
func (c *Client) ListAddressTransactions(addresses []btcutil.Address, account string) ([]btcjson.ListTransactionsResult, error) {
|
||||||
|
return c.ListAddressTransactionsAsync(addresses, account).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetBestBlockResult is a future promise to deliver the result of a
|
||||||
|
// GetBestBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetBestBlockResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the hash
|
||||||
|
// and height of the block in the longest (best) chain.
|
||||||
|
func (r FutureGetBestBlockResult) Receive() (*chainhash.Hash, int32, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getbestblock result object.
|
||||||
|
var bestBlock btcjson.GetBestBlockResult
|
||||||
|
err = json.Unmarshal(res, &bestBlock)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to hash from string.
|
||||||
|
hash, err := chainhash.NewHashFromStr(bestBlock.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash, bestBlock.Height, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBestBlockAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetBestBlock for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) GetBestBlockAsync() FutureGetBestBlockResult {
|
||||||
|
cmd := btcjson.NewGetBestBlockCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBestBlock returns the hash and height of the block in the longest (best)
|
||||||
|
// chain.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) GetBestBlock() (*chainhash.Hash, int32, error) {
|
||||||
|
return c.GetBestBlockAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetCurrentNetResult is a future promise to deliver the result of a
|
||||||
|
// GetCurrentNetAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetCurrentNetResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the network
|
||||||
|
// the server is running on.
|
||||||
|
func (r FutureGetCurrentNetResult) Receive() (wire.BitcoinNet, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an int64.
|
||||||
|
var net int64
|
||||||
|
err = json.Unmarshal(res, &net)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return wire.BitcoinNet(net), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentNetAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetCurrentNet for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) GetCurrentNetAsync() FutureGetCurrentNetResult {
|
||||||
|
cmd := btcjson.NewGetCurrentNetCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentNet returns the network the server is running on.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcd extension.
|
||||||
|
func (c *Client) GetCurrentNet() (wire.BitcoinNet, error) {
|
||||||
|
return c.GetCurrentNetAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetHeadersResult is a future promise to deliver the result of a
|
||||||
|
// getheaders RPC invocation (or an applicable error).
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
type FutureGetHeadersResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// getheaders result.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (r FutureGetHeadersResult) Receive() ([]wire.BlockHeader, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a slice of strings.
|
||||||
|
var result []string
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the []string into []wire.BlockHeader.
|
||||||
|
headers := make([]wire.BlockHeader, len(result))
|
||||||
|
for i, headerHex := range result {
|
||||||
|
serialized, err := hex.DecodeString(headerHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = headers[i].Deserialize(bytes.NewReader(serialized))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeadersAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetHeaders for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) GetHeadersAsync(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) FutureGetHeadersResult {
|
||||||
|
locators := make([]string, len(blockLocators))
|
||||||
|
for i := range blockLocators {
|
||||||
|
locators[i] = blockLocators[i].String()
|
||||||
|
}
|
||||||
|
hash := ""
|
||||||
|
if hashStop != nil {
|
||||||
|
hash = hashStop.String()
|
||||||
|
}
|
||||||
|
cmd := btcjson.NewGetHeadersCmd(locators, hash)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeaders mimics the wire protocol getheaders and headers messages by
|
||||||
|
// returning all headers on the main chain after the first known block in the
|
||||||
|
// locators, up until a block hash matches hashStop.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) GetHeaders(blockLocators []chainhash.Hash, hashStop *chainhash.Hash) ([]wire.BlockHeader, error) {
|
||||||
|
return c.GetHeadersAsync(blockLocators, hashStop).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureExportWatchingWalletResult is a future promise to deliver the result of
|
||||||
|
// an ExportWatchingWalletAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureExportWatchingWalletResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// exported wallet.
|
||||||
|
func (r FutureExportWatchingWalletResult) Receive() ([]byte, []byte, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a JSON object.
|
||||||
|
var obj map[string]interface{}
|
||||||
|
err = json.Unmarshal(res, &obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the wallet and tx string fields in the object.
|
||||||
|
base64Wallet, ok := obj["wallet"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected response type for "+
|
||||||
|
"exportwatchingwallet 'wallet' field: %T\n",
|
||||||
|
obj["wallet"])
|
||||||
|
}
|
||||||
|
base64TxStore, ok := obj["tx"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("unexpected response type for "+
|
||||||
|
"exportwatchingwallet 'tx' field: %T\n",
|
||||||
|
obj["tx"])
|
||||||
|
}
|
||||||
|
|
||||||
|
walletBytes, err := base64.StdEncoding.DecodeString(base64Wallet)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txStoreBytes, err := base64.StdEncoding.DecodeString(base64TxStore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return walletBytes, txStoreBytes, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportWatchingWalletAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See ExportWatchingWallet for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcwallet extension.
|
||||||
|
func (c *Client) ExportWatchingWalletAsync(account string) FutureExportWatchingWalletResult {
|
||||||
|
cmd := btcjson.NewExportWatchingWalletCmd(&account, btcjson.Bool(true))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExportWatchingWallet returns the raw bytes for a watching-only version of
|
||||||
|
// wallet.bin and tx.bin, respectively, for the specified account that can be
|
||||||
|
// used by btcwallet to enable a wallet which does not have the private keys
|
||||||
|
// necessary to spend funds.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcwallet extension.
|
||||||
|
func (c *Client) ExportWatchingWallet(account string) ([]byte, []byte, error) {
|
||||||
|
return c.ExportWatchingWalletAsync(account).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSessionResult is a future promise to deliver the result of a
|
||||||
|
// SessionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureSessionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// session result.
|
||||||
|
func (r FutureSessionResult) Receive() (*btcjson.SessionResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a session result object.
|
||||||
|
var session btcjson.SessionResult
|
||||||
|
err = json.Unmarshal(res, &session)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See Session for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension.
|
||||||
|
func (c *Client) SessionAsync() FutureSessionResult {
|
||||||
|
// Not supported in HTTP POST mode.
|
||||||
|
if c.config.HTTPPostMode {
|
||||||
|
return newFutureError(ErrWebsocketsRequired)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSessionCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session returns details regarding a websocket client's current connection.
|
||||||
|
//
|
||||||
|
// This RPC requires the client to be running in websocket mode.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension.
|
||||||
|
func (c *Client) Session() (*btcjson.SessionResult, error) {
|
||||||
|
return c.SessionAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureVersionResult is a future promise to deliver the result of a version
|
||||||
|
// RPC invocation (or an applicable error).
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
type FutureVersionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the version
|
||||||
|
// result.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (r FutureVersionResult) Receive() (map[string]btcjson.VersionResult,
|
||||||
|
error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a version result object.
|
||||||
|
var vr map[string]btcjson.VersionResult
|
||||||
|
err = json.Unmarshal(res, &vr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See Version for the blocking version and more details.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) VersionAsync() FutureVersionResult {
|
||||||
|
cmd := btcjson.NewVersionCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns information about the server's JSON-RPC API versions.
|
||||||
|
//
|
||||||
|
// NOTE: This is a btcsuite extension ported from
|
||||||
|
// github.com/decred/dcrrpcclient.
|
||||||
|
func (c *Client) Version() (map[string]btcjson.VersionResult, error) {
|
||||||
|
return c.VersionAsync().Receive()
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,47 @@
|
||||||
|
// 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 rpcclient
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogClosure is a closure that can be printed with %v to be used to
|
||||||
|
// generate expensive-to-create data for a detailed log level and avoid doing
|
||||||
|
// the work if the data isn't printed.
|
||||||
|
type logClosure func() string
|
||||||
|
|
||||||
|
// String invokes the log closure and returns the results string.
|
||||||
|
func (c logClosure) String() string {
|
||||||
|
return c()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLogClosure returns a new closure over the passed function which allows
|
||||||
|
// it to be used as a parameter in a logging function that is only invoked when
|
||||||
|
// the logging level is such that the message will actually be logged.
|
||||||
|
func newLogClosure(c func() string) logClosure {
|
||||||
|
return logClosure(c)
|
||||||
|
}
|
|
@ -0,0 +1,417 @@
|
||||||
|
// 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 rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FutureGenerateResult is a future promise to deliver the result of a
|
||||||
|
// GenerateAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGenerateResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a list of
|
||||||
|
// block hashes generated by the call.
|
||||||
|
func (r FutureGenerateResult) Receive() ([]*chainhash.Hash, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a list of strings.
|
||||||
|
var result []string
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert each block hash to a chainhash.Hash and store a pointer to
|
||||||
|
// each.
|
||||||
|
convertedResult := make([]*chainhash.Hash, len(result))
|
||||||
|
for i, hashString := range result {
|
||||||
|
convertedResult[i], err = chainhash.NewHashFromStr(hashString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertedResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See Generate for the blocking version and more details.
|
||||||
|
func (c *Client) GenerateAsync(numBlocks uint32) FutureGenerateResult {
|
||||||
|
cmd := btcjson.NewGenerateCmd(numBlocks)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate generates numBlocks blocks and returns their hashes.
|
||||||
|
func (c *Client) Generate(numBlocks uint32) ([]*chainhash.Hash, error) {
|
||||||
|
return c.GenerateAsync(numBlocks).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetGenerateResult is a future promise to deliver the result of a
|
||||||
|
// GetGenerateAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetGenerateResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns true if the
|
||||||
|
// server is set to mine, otherwise false.
|
||||||
|
func (r FutureGetGenerateResult) Receive() (bool, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a boolean.
|
||||||
|
var result bool
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGenerateAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetGenerate for the blocking version and more details.
|
||||||
|
func (c *Client) GetGenerateAsync() FutureGetGenerateResult {
|
||||||
|
cmd := btcjson.NewGetGenerateCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGenerate returns true if the server is set to mine, otherwise false.
|
||||||
|
func (c *Client) GetGenerate() (bool, error) {
|
||||||
|
return c.GetGenerateAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSetGenerateResult is a future promise to deliver the result of a
|
||||||
|
// SetGenerateAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureSetGenerateResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns an error if
|
||||||
|
// any occurred when setting the server to generate coins (mine) or not.
|
||||||
|
func (r FutureSetGenerateResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGenerateAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See SetGenerate for the blocking version and more details.
|
||||||
|
func (c *Client) SetGenerateAsync(enable bool, numCPUs int) FutureSetGenerateResult {
|
||||||
|
cmd := btcjson.NewSetGenerateCmd(enable, &numCPUs)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGenerate sets the server to generate coins (mine) or not.
|
||||||
|
func (c *Client) SetGenerate(enable bool, numCPUs int) error {
|
||||||
|
return c.SetGenerateAsync(enable, numCPUs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetHashesPerSecResult is a future promise to deliver the result of a
|
||||||
|
// GetHashesPerSecAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetHashesPerSecResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a recent
|
||||||
|
// hashes per second performance measurement while generating coins (mining).
|
||||||
|
// Zero is returned if the server is not mining.
|
||||||
|
func (r FutureGetHashesPerSecResult) Receive() (int64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an int64.
|
||||||
|
var result int64
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHashesPerSecAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetHashesPerSec for the blocking version and more details.
|
||||||
|
func (c *Client) GetHashesPerSecAsync() FutureGetHashesPerSecResult {
|
||||||
|
cmd := btcjson.NewGetHashesPerSecCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHashesPerSec returns a recent hashes per second performance measurement
|
||||||
|
// while generating coins (mining). Zero is returned if the server is not
|
||||||
|
// mining.
|
||||||
|
func (c *Client) GetHashesPerSec() (int64, error) {
|
||||||
|
return c.GetHashesPerSecAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetMiningInfoResult is a future promise to deliver the result of a
|
||||||
|
// GetMiningInfoAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetMiningInfoResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the mining
|
||||||
|
// information.
|
||||||
|
func (r FutureGetMiningInfoResult) Receive() (*btcjson.GetMiningInfoResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getmininginfo result object.
|
||||||
|
var infoResult btcjson.GetMiningInfoResult
|
||||||
|
err = json.Unmarshal(res, &infoResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &infoResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMiningInfoAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetMiningInfo for the blocking version and more details.
|
||||||
|
func (c *Client) GetMiningInfoAsync() FutureGetMiningInfoResult {
|
||||||
|
cmd := btcjson.NewGetMiningInfoCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMiningInfo returns mining information.
|
||||||
|
func (c *Client) GetMiningInfo() (*btcjson.GetMiningInfoResult, error) {
|
||||||
|
return c.GetMiningInfoAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetNetworkHashPS is a future promise to deliver the result of a
|
||||||
|
// GetNetworkHashPSAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetNetworkHashPS chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// estimated network hashes per second for the block heights provided by the
|
||||||
|
// parameters.
|
||||||
|
func (r FutureGetNetworkHashPS) Receive() (int64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an int64.
|
||||||
|
var result int64
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPSAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS for the blocking version and more details.
|
||||||
|
func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS {
|
||||||
|
cmd := btcjson.NewGetNetworkHashPSCmd(nil, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPS returns the estimated network hashes per second using the
|
||||||
|
// default number of blocks and the most recent block height.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS2 to override the number of blocks to use and
|
||||||
|
// GetNetworkHashPS3 to override the height at which to calculate the estimate.
|
||||||
|
func (c *Client) GetNetworkHashPS() (int64, error) {
|
||||||
|
return c.GetNetworkHashPSAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPS2Async returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS2 for the blocking version and more details.
|
||||||
|
func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS {
|
||||||
|
cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPS2 returns the estimated network hashes per second for the
|
||||||
|
// specified previous number of blocks working backwards from the most recent
|
||||||
|
// block height. The blocks parameter can also be -1 in which case the number
|
||||||
|
// of blocks since the last difficulty change will be used.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS to use defaults and GetNetworkHashPS3 to override the
|
||||||
|
// height at which to calculate the estimate.
|
||||||
|
func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) {
|
||||||
|
return c.GetNetworkHashPS2Async(blocks).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPS3Async returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS3 for the blocking version and more details.
|
||||||
|
func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHashPS {
|
||||||
|
cmd := btcjson.NewGetNetworkHashPSCmd(&blocks, &height)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkHashPS3 returns the estimated network hashes per second for the
|
||||||
|
// specified previous number of blocks working backwards from the specified
|
||||||
|
// block height. The blocks parameter can also be -1 in which case the number
|
||||||
|
// of blocks since the last difficulty change will be used.
|
||||||
|
//
|
||||||
|
// See GetNetworkHashPS and GetNetworkHashPS2 to use defaults.
|
||||||
|
func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) {
|
||||||
|
return c.GetNetworkHashPS3Async(blocks, height).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetWork is a future promise to deliver the result of a
|
||||||
|
// GetWorkAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetWork chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the hash
|
||||||
|
// data to work on.
|
||||||
|
func (r FutureGetWork) Receive() (*btcjson.GetWorkResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getwork result object.
|
||||||
|
var result btcjson.GetWorkResult
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetWork for the blocking version and more details.
|
||||||
|
func (c *Client) GetWorkAsync() FutureGetWork {
|
||||||
|
cmd := btcjson.NewGetWorkCmd(nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWork returns hash data to work on.
|
||||||
|
//
|
||||||
|
// See GetWorkSubmit to submit the found solution.
|
||||||
|
func (c *Client) GetWork() (*btcjson.GetWorkResult, error) {
|
||||||
|
return c.GetWorkAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetWorkSubmit is a future promise to deliver the result of a
|
||||||
|
// GetWorkSubmitAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetWorkSubmit chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns whether
|
||||||
|
// or not the submitted block header was accepted.
|
||||||
|
func (r FutureGetWorkSubmit) Receive() (bool, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a boolean.
|
||||||
|
var accepted bool
|
||||||
|
err = json.Unmarshal(res, &accepted)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return accepted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkSubmitAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetWorkSubmit for the blocking version and more details.
|
||||||
|
func (c *Client) GetWorkSubmitAsync(data string) FutureGetWorkSubmit {
|
||||||
|
cmd := btcjson.NewGetWorkCmd(&data)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWorkSubmit submits a block header which is a solution to previously
|
||||||
|
// requested data and returns whether or not the solution was accepted.
|
||||||
|
//
|
||||||
|
// See GetWork to request data to work on.
|
||||||
|
func (c *Client) GetWorkSubmit(data string) (bool, error) {
|
||||||
|
return c.GetWorkSubmitAsync(data).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSubmitBlockResult is a future promise to deliver the result of a
|
||||||
|
// SubmitBlockAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureSubmitBlockResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns an error if
|
||||||
|
// any occurred when submitting the block.
|
||||||
|
func (r FutureSubmitBlockResult) Receive() error {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(res) != "null" {
|
||||||
|
var result string
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitBlockAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See SubmitBlock for the blocking version and more details.
|
||||||
|
func (c *Client) SubmitBlockAsync(block *btcutil.Block, options *btcjson.SubmitBlockOptions) FutureSubmitBlockResult {
|
||||||
|
blockHex := ""
|
||||||
|
if block != nil {
|
||||||
|
blockBytes, err := block.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockHex = hex.EncodeToString(blockBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSubmitBlockCmd(blockHex, options)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitBlock attempts to submit a new block into the bitcoin network.
|
||||||
|
func (c *Client) SubmitBlock(block *btcutil.Block, options *btcjson.SubmitBlockOptions) error {
|
||||||
|
return c.SubmitBlockAsync(block, options).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(davec): Implement GetBlockTemplate
|
|
@ -0,0 +1,319 @@
|
||||||
|
// 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 rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddNodeCommand enumerates the available commands that the AddNode function
|
||||||
|
// accepts.
|
||||||
|
type AddNodeCommand string
|
||||||
|
|
||||||
|
// Constants used to indicate the command for the AddNode function.
|
||||||
|
const (
|
||||||
|
// ANAdd indicates the specified host should be added as a persistent
|
||||||
|
// peer.
|
||||||
|
ANAdd AddNodeCommand = "add"
|
||||||
|
|
||||||
|
// ANRemove indicates the specified peer should be removed.
|
||||||
|
ANRemove AddNodeCommand = "remove"
|
||||||
|
|
||||||
|
// ANOneTry indicates the specified host should try to connect once,
|
||||||
|
// but it should not be made persistent.
|
||||||
|
ANOneTry AddNodeCommand = "onetry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the AddNodeCommand in human-readable form.
|
||||||
|
func (cmd AddNodeCommand) String() string {
|
||||||
|
return string(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureAddNodeResult is a future promise to deliver the result of an
|
||||||
|
// AddNodeAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureAddNodeResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns an error if
|
||||||
|
// any occurred when performing the specified command.
|
||||||
|
func (r FutureAddNodeResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNodeAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See AddNode for the blocking version and more details.
|
||||||
|
func (c *Client) AddNodeAsync(host string, command AddNodeCommand) FutureAddNodeResult {
|
||||||
|
cmd := btcjson.NewAddNodeCmd(host, btcjson.AddNodeSubCmd(command))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNode attempts to perform the passed command on the passed persistent peer.
|
||||||
|
// For example, it can be used to add or a remove a persistent peer, or to do
|
||||||
|
// a one time connection to a peer.
|
||||||
|
//
|
||||||
|
// It may not be used to remove non-persistent peers.
|
||||||
|
func (c *Client) AddNode(host string, command AddNodeCommand) error {
|
||||||
|
return c.AddNodeAsync(host, command).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureNodeResult is a future promise to deliver the result of a NodeAsync
|
||||||
|
// RPC invocation (or an applicable error).
|
||||||
|
type FutureNodeResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns an error if
|
||||||
|
// any occurred when performing the specified command.
|
||||||
|
func (r FutureNodeResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAsync returns an instance of a type that can be used to get the result
|
||||||
|
// of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See Node for the blocking version and more details.
|
||||||
|
func (c *Client) NodeAsync(command btcjson.NodeSubCmd, host string,
|
||||||
|
connectSubCmd *string) FutureNodeResult {
|
||||||
|
cmd := btcjson.NewNodeCmd(command, host, connectSubCmd)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Node attempts to perform the passed node command on the host.
|
||||||
|
// For example, it can be used to add or a remove a persistent peer, or to do
|
||||||
|
// connect or diconnect a non-persistent one.
|
||||||
|
//
|
||||||
|
// The connectSubCmd should be set either "perm" or "temp", depending on
|
||||||
|
// whether we are targetting a persistent or non-persistent peer. Passing nil
|
||||||
|
// will cause the default value to be used, which currently is "temp".
|
||||||
|
func (c *Client) Node(command btcjson.NodeSubCmd, host string,
|
||||||
|
connectSubCmd *string) error {
|
||||||
|
return c.NodeAsync(command, host, connectSubCmd).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetAddedNodeInfoResult is a future promise to deliver the result of a
|
||||||
|
// GetAddedNodeInfoAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetAddedNodeInfoResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about manually added (persistent) peers.
|
||||||
|
func (r FutureGetAddedNodeInfoResult) Receive() ([]btcjson.GetAddedNodeInfoResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal as an array of getaddednodeinfo result objects.
|
||||||
|
var nodeInfo []btcjson.GetAddedNodeInfoResult
|
||||||
|
err = json.Unmarshal(res, &nodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddedNodeInfoAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetAddedNodeInfo for the blocking version and more details.
|
||||||
|
func (c *Client) GetAddedNodeInfoAsync(peer string) FutureGetAddedNodeInfoResult {
|
||||||
|
cmd := btcjson.NewGetAddedNodeInfoCmd(true, &peer)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddedNodeInfo returns information about manually added (persistent) peers.
|
||||||
|
//
|
||||||
|
// See GetAddedNodeInfoNoDNS to retrieve only a list of the added (persistent)
|
||||||
|
// peers.
|
||||||
|
func (c *Client) GetAddedNodeInfo(peer string) ([]btcjson.GetAddedNodeInfoResult, error) {
|
||||||
|
return c.GetAddedNodeInfoAsync(peer).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetAddedNodeInfoNoDNSResult is a future promise to deliver the result
|
||||||
|
// of a GetAddedNodeInfoNoDNSAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetAddedNodeInfoNoDNSResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a list of
|
||||||
|
// manually added (persistent) peers.
|
||||||
|
func (r FutureGetAddedNodeInfoNoDNSResult) Receive() ([]string, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an array of strings.
|
||||||
|
var nodes []string
|
||||||
|
err = json.Unmarshal(res, &nodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddedNodeInfoNoDNSAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetAddedNodeInfoNoDNS for the blocking version and more details.
|
||||||
|
func (c *Client) GetAddedNodeInfoNoDNSAsync(peer string) FutureGetAddedNodeInfoNoDNSResult {
|
||||||
|
cmd := btcjson.NewGetAddedNodeInfoCmd(false, &peer)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddedNodeInfoNoDNS returns a list of manually added (persistent) peers.
|
||||||
|
// This works by setting the dns flag to false in the underlying RPC.
|
||||||
|
//
|
||||||
|
// See GetAddedNodeInfo to obtain more information about each added (persistent)
|
||||||
|
// peer.
|
||||||
|
func (c *Client) GetAddedNodeInfoNoDNS(peer string) ([]string, error) {
|
||||||
|
return c.GetAddedNodeInfoNoDNSAsync(peer).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetConnectionCountResult is a future promise to deliver the result
|
||||||
|
// of a GetConnectionCountAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetConnectionCountResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the number
|
||||||
|
// of active connections to other peers.
|
||||||
|
func (r FutureGetConnectionCountResult) Receive() (int64, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an int64.
|
||||||
|
var count int64
|
||||||
|
err = json.Unmarshal(res, &count)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnectionCountAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetConnectionCount for the blocking version and more details.
|
||||||
|
func (c *Client) GetConnectionCountAsync() FutureGetConnectionCountResult {
|
||||||
|
cmd := btcjson.NewGetConnectionCountCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnectionCount returns the number of active connections to other peers.
|
||||||
|
func (c *Client) GetConnectionCount() (int64, error) {
|
||||||
|
return c.GetConnectionCountAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuturePingResult is a future promise to deliver the result of a PingAsync RPC
|
||||||
|
// invocation (or an applicable error).
|
||||||
|
type FuturePingResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the result
|
||||||
|
// of queueing a ping to be sent to each connected peer.
|
||||||
|
func (r FuturePingResult) Receive() error {
|
||||||
|
_, err := receiveFuture(r)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PingAsync returns an instance of a type that can be used to get the result of
|
||||||
|
// the RPC at some future time by invoking the Receive function on the returned
|
||||||
|
// instance.
|
||||||
|
//
|
||||||
|
// See Ping for the blocking version and more details.
|
||||||
|
func (c *Client) PingAsync() FuturePingResult {
|
||||||
|
cmd := btcjson.NewPingCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping queues a ping to be sent to each connected peer.
|
||||||
|
//
|
||||||
|
// Use the GetPeerInfo function and examine the PingTime and PingWait fields to
|
||||||
|
// access the ping times.
|
||||||
|
func (c *Client) Ping() error {
|
||||||
|
return c.PingAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetPeerInfoResult is a future promise to deliver the result of a
|
||||||
|
// GetPeerInfoAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetPeerInfoResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns data about
|
||||||
|
// each connected network peer.
|
||||||
|
func (r FutureGetPeerInfoResult) Receive() ([]btcjson.GetPeerInfoResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as an array of getpeerinfo result objects.
|
||||||
|
var peerInfo []btcjson.GetPeerInfoResult
|
||||||
|
err = json.Unmarshal(res, &peerInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return peerInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerInfoAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetPeerInfo for the blocking version and more details.
|
||||||
|
func (c *Client) GetPeerInfoAsync() FutureGetPeerInfoResult {
|
||||||
|
cmd := btcjson.NewGetPeerInfoCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerInfo returns data about each connected network peer.
|
||||||
|
func (c *Client) GetPeerInfo() ([]btcjson.GetPeerInfoResult, error) {
|
||||||
|
return c.GetPeerInfoAsync().Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetNetTotalsResult is a future promise to deliver the result of a
|
||||||
|
// GetNetTotalsAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetNetTotalsResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns network
|
||||||
|
// traffic statistics.
|
||||||
|
func (r FutureGetNetTotalsResult) Receive() (*btcjson.GetNetTotalsResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a getnettotals result object.
|
||||||
|
var totals btcjson.GetNetTotalsResult
|
||||||
|
err = json.Unmarshal(res, &totals)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &totals, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetTotalsAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of the RPC at some future time by invoking the Receive function on the
|
||||||
|
// returned instance.
|
||||||
|
//
|
||||||
|
// See GetNetTotals for the blocking version and more details.
|
||||||
|
func (c *Client) GetNetTotalsAsync() FutureGetNetTotalsResult {
|
||||||
|
cmd := btcjson.NewGetNetTotalsCmd()
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetTotals returns network traffic statistics.
|
||||||
|
func (c *Client) GetNetTotals() (*btcjson.GetNetTotalsResult, error) {
|
||||||
|
return c.GetNetTotalsAsync().Receive()
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,78 @@
|
||||||
|
// 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 rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FutureRawResult is a future promise to deliver the result of a RawRequest RPC
|
||||||
|
// invocation (or an applicable error).
|
||||||
|
type FutureRawResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the raw
|
||||||
|
// response, or an error if the request was unsuccessful.
|
||||||
|
func (r FutureRawResult) Receive() (json.RawMessage, error) {
|
||||||
|
return receiveFuture(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawRequestAsync returns an instance of a type that can be used to get the
|
||||||
|
// result of a custom RPC request at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See RawRequest for the blocking version and more details.
|
||||||
|
func (c *Client) RawRequestAsync(method string, params []json.RawMessage) FutureRawResult {
|
||||||
|
// Method may not be empty.
|
||||||
|
if method == "" {
|
||||||
|
return newFutureError(errors.New("no method"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal parameters as "[]" instead of "null" when no parameters
|
||||||
|
// are passed.
|
||||||
|
if params == nil {
|
||||||
|
params = []json.RawMessage{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a raw JSON-RPC request using the provided method and params
|
||||||
|
// and marshal it. This is done rather than using the sendCmd function
|
||||||
|
// since that relies on marshalling registered btcjson commands rather
|
||||||
|
// than custom commands.
|
||||||
|
id := c.NextID()
|
||||||
|
rawRequest := &btcjson.Request{
|
||||||
|
Jsonrpc: "1.0",
|
||||||
|
ID: id,
|
||||||
|
Method: method,
|
||||||
|
Params: params,
|
||||||
|
}
|
||||||
|
marshalledJSON, err := json.Marshal(rawRequest)
|
||||||
|
if err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the request and send it along with a channel to respond on.
|
||||||
|
responseChan := make(chan *response, 1)
|
||||||
|
jReq := &jsonRequest{
|
||||||
|
id: id,
|
||||||
|
method: method,
|
||||||
|
cmd: nil,
|
||||||
|
marshalledJSON: marshalledJSON,
|
||||||
|
responseChan: responseChan,
|
||||||
|
}
|
||||||
|
c.sendRequest(jReq)
|
||||||
|
|
||||||
|
return responseChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawRequest allows the caller to send a raw or custom request to the server.
|
||||||
|
// This method may be used to send and receive requests and responses for
|
||||||
|
// requests that are not handled by this client package, or to proxy partially
|
||||||
|
// unmarshaled requests to another JSON-RPC server if a request cannot be
|
||||||
|
// handled directly.
|
||||||
|
func (c *Client) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) {
|
||||||
|
return c.RawRequestAsync(method, params).Receive()
|
||||||
|
}
|
|
@ -0,0 +1,664 @@
|
||||||
|
// 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 rpcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SigHashType enumerates the available signature hashing types that the
|
||||||
|
// SignRawTransaction function accepts.
|
||||||
|
type SigHashType string
|
||||||
|
|
||||||
|
// Constants used to indicate the signature hash type for SignRawTransaction.
|
||||||
|
const (
|
||||||
|
// SigHashAll indicates ALL of the outputs should be signed.
|
||||||
|
SigHashAll SigHashType = "ALL"
|
||||||
|
|
||||||
|
// SigHashNone indicates NONE of the outputs should be signed. This
|
||||||
|
// can be thought of as specifying the signer does not care where the
|
||||||
|
// bitcoins go.
|
||||||
|
SigHashNone SigHashType = "NONE"
|
||||||
|
|
||||||
|
// SigHashSingle indicates that a SINGLE output should be signed. This
|
||||||
|
// can be thought of specifying the signer only cares about where ONE of
|
||||||
|
// the outputs goes, but not any of the others.
|
||||||
|
SigHashSingle SigHashType = "SINGLE"
|
||||||
|
|
||||||
|
// SigHashAllAnyoneCanPay indicates that signer does not care where the
|
||||||
|
// other inputs to the transaction come from, so it allows other people
|
||||||
|
// to add inputs. In addition, it uses the SigHashAll signing method
|
||||||
|
// for outputs.
|
||||||
|
SigHashAllAnyoneCanPay SigHashType = "ALL|ANYONECANPAY"
|
||||||
|
|
||||||
|
// SigHashNoneAnyoneCanPay indicates that signer does not care where the
|
||||||
|
// other inputs to the transaction come from, so it allows other people
|
||||||
|
// to add inputs. In addition, it uses the SigHashNone signing method
|
||||||
|
// for outputs.
|
||||||
|
SigHashNoneAnyoneCanPay SigHashType = "NONE|ANYONECANPAY"
|
||||||
|
|
||||||
|
// SigHashSingleAnyoneCanPay indicates that signer does not care where
|
||||||
|
// the other inputs to the transaction come from, so it allows other
|
||||||
|
// people to add inputs. In addition, it uses the SigHashSingle signing
|
||||||
|
// method for outputs.
|
||||||
|
SigHashSingleAnyoneCanPay SigHashType = "SINGLE|ANYONECANPAY"
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns the SighHashType in human-readable form.
|
||||||
|
func (s SigHashType) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetRawTransactionResult is a future promise to deliver the result of a
|
||||||
|
// GetRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureGetRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a
|
||||||
|
// transaction given its hash.
|
||||||
|
func (r FutureGetRawTransactionResult) Receive() (*btcutil.Tx, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var txHex string
|
||||||
|
err = json.Unmarshal(res, &txHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the serialized transaction hex to raw bytes.
|
||||||
|
serializedTx, err := hex.DecodeString(txHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction and return it.
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return btcutil.NewTx(&msgTx), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTransactionAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See GetRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) GetRawTransactionAsync(txHash *chainhash.Hash) FutureGetRawTransactionResult {
|
||||||
|
hash := ""
|
||||||
|
if txHash != nil {
|
||||||
|
hash = txHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(0))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTransaction returns a transaction given its hash.
|
||||||
|
//
|
||||||
|
// See GetRawTransactionVerbose to obtain additional information about the
|
||||||
|
// transaction.
|
||||||
|
func (c *Client) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) {
|
||||||
|
return c.GetRawTransactionAsync(txHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureGetRawTransactionVerboseResult is a future promise to deliver the
|
||||||
|
// result of a GetRawTransactionVerboseAsync RPC invocation (or an applicable
|
||||||
|
// error).
|
||||||
|
type FutureGetRawTransactionVerboseResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about a transaction given its hash.
|
||||||
|
func (r FutureGetRawTransactionVerboseResult) Receive() (*btcjson.TxRawResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a gettrawtransaction result object.
|
||||||
|
var rawTxResult btcjson.TxRawResult
|
||||||
|
err = json.Unmarshal(res, &rawTxResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rawTxResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTransactionVerboseAsync returns an instance of a type that can be used
|
||||||
|
// to get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See GetRawTransactionVerbose for the blocking version and more details.
|
||||||
|
func (c *Client) GetRawTransactionVerboseAsync(txHash *chainhash.Hash) FutureGetRawTransactionVerboseResult {
|
||||||
|
hash := ""
|
||||||
|
if txHash != nil {
|
||||||
|
hash = txHash.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewGetRawTransactionCmd(hash, btcjson.Int(1))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRawTransactionVerbose returns information about a transaction given
|
||||||
|
// its hash.
|
||||||
|
//
|
||||||
|
// See GetRawTransaction to obtain only the transaction already deserialized.
|
||||||
|
func (c *Client) GetRawTransactionVerbose(txHash *chainhash.Hash) (*btcjson.TxRawResult, error) {
|
||||||
|
return c.GetRawTransactionVerboseAsync(txHash).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureDecodeRawTransactionResult is a future promise to deliver the result
|
||||||
|
// of a DecodeRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureDecodeRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about a transaction given its serialized bytes.
|
||||||
|
func (r FutureDecodeRawTransactionResult) Receive() (*btcjson.TxRawResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a decoderawtransaction result object.
|
||||||
|
var rawTxResult btcjson.TxRawResult
|
||||||
|
err = json.Unmarshal(res, &rawTxResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rawTxResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeRawTransactionAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See DecodeRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) DecodeRawTransactionAsync(serializedTx []byte) FutureDecodeRawTransactionResult {
|
||||||
|
txHex := hex.EncodeToString(serializedTx)
|
||||||
|
cmd := btcjson.NewDecodeRawTransactionCmd(txHex)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeRawTransaction returns information about a transaction given its
|
||||||
|
// serialized bytes.
|
||||||
|
func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult, error) {
|
||||||
|
return c.DecodeRawTransactionAsync(serializedTx).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureCreateRawTransactionResult is a future promise to deliver the result
|
||||||
|
// of a CreateRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureCreateRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns a new
|
||||||
|
// transaction spending the provided inputs and sending to the provided
|
||||||
|
// addresses.
|
||||||
|
func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var txHex string
|
||||||
|
err = json.Unmarshal(res, &txHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the serialized transaction hex to raw bytes.
|
||||||
|
serializedTx, err := hex.DecodeString(txHex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction and return it.
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &msgTx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRawTransactionAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See CreateRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput,
|
||||||
|
amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) FutureCreateRawTransactionResult {
|
||||||
|
|
||||||
|
convertedAmts := make(map[string]float64, len(amounts))
|
||||||
|
for addr, amount := range amounts {
|
||||||
|
convertedAmts[addr.String()] = amount.ToBTC()
|
||||||
|
}
|
||||||
|
cmd := btcjson.NewCreateRawTransactionCmd(inputs, convertedAmts, lockTime)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRawTransaction returns a new transaction spending the provided inputs
|
||||||
|
// and sending to the provided addresses.
|
||||||
|
func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput,
|
||||||
|
amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) (*wire.MsgTx, error) {
|
||||||
|
|
||||||
|
return c.CreateRawTransactionAsync(inputs, amounts, lockTime).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSendRawTransactionResult is a future promise to deliver the result
|
||||||
|
// of a SendRawTransactionAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureSendRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the result
|
||||||
|
// of submitting the encoded transaction to the server which then relays it to
|
||||||
|
// the network.
|
||||||
|
func (r FutureSendRawTransactionResult) Receive() (*chainhash.Hash, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a string.
|
||||||
|
var txHashStr string
|
||||||
|
err = json.Unmarshal(res, &txHashStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return chainhash.NewHashFromStr(txHashStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendRawTransactionAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See SendRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) SendRawTransactionAsync(tx *wire.MsgTx, allowHighFees bool) FutureSendRawTransactionResult {
|
||||||
|
txHex := ""
|
||||||
|
if tx != nil {
|
||||||
|
// Serialize the transaction and convert to hex string.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
||||||
|
if err := tx.Serialize(buf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
txHex = hex.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSendRawTransactionCmd(txHex, &allowHighFees)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendRawTransaction submits the encoded transaction to the server which will
|
||||||
|
// then relay it to the network.
|
||||||
|
func (c *Client) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) {
|
||||||
|
return c.SendRawTransactionAsync(tx, allowHighFees).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSignRawTransactionResult is a future promise to deliver the result
|
||||||
|
// of one of the SignRawTransactionAsync family of RPC invocations (or an
|
||||||
|
// applicable error).
|
||||||
|
type FutureSignRawTransactionResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// signed transaction as well as whether or not all inputs are now signed.
|
||||||
|
func (r FutureSignRawTransactionResult) Receive() (*wire.MsgTx, bool, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal as a signrawtransaction result.
|
||||||
|
var signRawTxResult btcjson.SignRawTransactionResult
|
||||||
|
err = json.Unmarshal(res, &signRawTxResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the serialized transaction hex to raw bytes.
|
||||||
|
serializedTx, err := hex.DecodeString(signRawTxResult.Hex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction and return it.
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &msgTx, signRawTxResult.Complete, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransactionAsync returns an instance of a type that can be used to get
|
||||||
|
// the result of the RPC at some future time by invoking the Receive function on
|
||||||
|
// the returned instance.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction for the blocking version and more details.
|
||||||
|
func (c *Client) SignRawTransactionAsync(tx *wire.MsgTx) FutureSignRawTransactionResult {
|
||||||
|
txHex := ""
|
||||||
|
if tx != nil {
|
||||||
|
// Serialize the transaction and convert to hex string.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
||||||
|
if err := tx.Serialize(buf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
txHex = hex.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSignRawTransactionCmd(txHex, nil, nil, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction signs inputs for the passed transaction and returns the
|
||||||
|
// signed transaction as well as whether or not all inputs are now signed.
|
||||||
|
//
|
||||||
|
// This function assumes the RPC server already knows the input transactions and
|
||||||
|
// private keys for the passed transaction which needs to be signed and uses the
|
||||||
|
// default signature hash type. Use one of the SignRawTransaction# variants to
|
||||||
|
// specify that information if needed.
|
||||||
|
func (c *Client) SignRawTransaction(tx *wire.MsgTx) (*wire.MsgTx, bool, error) {
|
||||||
|
return c.SignRawTransactionAsync(tx).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction2Async returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction2 for the blocking version and more details.
|
||||||
|
func (c *Client) SignRawTransaction2Async(tx *wire.MsgTx, inputs []btcjson.RawTxInput) FutureSignRawTransactionResult {
|
||||||
|
txHex := ""
|
||||||
|
if tx != nil {
|
||||||
|
// Serialize the transaction and convert to hex string.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
||||||
|
if err := tx.Serialize(buf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
txHex = hex.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, nil, nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction2 signs inputs for the passed transaction given the list
|
||||||
|
// of information about the input transactions needed to perform the signing
|
||||||
|
// process.
|
||||||
|
//
|
||||||
|
// This only input transactions that need to be specified are ones the
|
||||||
|
// RPC server does not already know. Already known input transactions will be
|
||||||
|
// merged with the specified transactions.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction if the RPC server already knows the input
|
||||||
|
// transactions.
|
||||||
|
func (c *Client) SignRawTransaction2(tx *wire.MsgTx, inputs []btcjson.RawTxInput) (*wire.MsgTx, bool, error) {
|
||||||
|
return c.SignRawTransaction2Async(tx, inputs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction3Async returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction3 for the blocking version and more details.
|
||||||
|
func (c *Client) SignRawTransaction3Async(tx *wire.MsgTx,
|
||||||
|
inputs []btcjson.RawTxInput,
|
||||||
|
privKeysWIF []string) FutureSignRawTransactionResult {
|
||||||
|
|
||||||
|
txHex := ""
|
||||||
|
if tx != nil {
|
||||||
|
// Serialize the transaction and convert to hex string.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
||||||
|
if err := tx.Serialize(buf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
txHex = hex.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF,
|
||||||
|
nil)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction3 signs inputs for the passed transaction given the list
|
||||||
|
// of information about extra input transactions and a list of private keys
|
||||||
|
// needed to perform the signing process. The private keys must be in wallet
|
||||||
|
// import format (WIF).
|
||||||
|
//
|
||||||
|
// This only input transactions that need to be specified are ones the
|
||||||
|
// RPC server does not already know. Already known input transactions will be
|
||||||
|
// merged with the specified transactions. This means the list of transaction
|
||||||
|
// inputs can be nil if the RPC server already knows them all.
|
||||||
|
//
|
||||||
|
// NOTE: Unlike the merging functionality of the input transactions, ONLY the
|
||||||
|
// specified private keys will be used, so even if the server already knows some
|
||||||
|
// of the private keys, they will NOT be used.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction if the RPC server already knows the input
|
||||||
|
// transactions and private keys or SignRawTransaction2 if it already knows the
|
||||||
|
// private keys.
|
||||||
|
func (c *Client) SignRawTransaction3(tx *wire.MsgTx,
|
||||||
|
inputs []btcjson.RawTxInput,
|
||||||
|
privKeysWIF []string) (*wire.MsgTx, bool, error) {
|
||||||
|
|
||||||
|
return c.SignRawTransaction3Async(tx, inputs, privKeysWIF).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction4Async returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See SignRawTransaction4 for the blocking version and more details.
|
||||||
|
func (c *Client) SignRawTransaction4Async(tx *wire.MsgTx,
|
||||||
|
inputs []btcjson.RawTxInput, privKeysWIF []string,
|
||||||
|
hashType SigHashType) FutureSignRawTransactionResult {
|
||||||
|
|
||||||
|
txHex := ""
|
||||||
|
if tx != nil {
|
||||||
|
// Serialize the transaction and convert to hex string.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, tx.SerializeSize()))
|
||||||
|
if err := tx.Serialize(buf); err != nil {
|
||||||
|
return newFutureError(err)
|
||||||
|
}
|
||||||
|
txHex = hex.EncodeToString(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := btcjson.NewSignRawTransactionCmd(txHex, &inputs, &privKeysWIF,
|
||||||
|
btcjson.String(string(hashType)))
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRawTransaction4 signs inputs for the passed transaction using the
|
||||||
|
// the specified signature hash type given the list of information about extra
|
||||||
|
// input transactions and a potential list of private keys needed to perform
|
||||||
|
// the signing process. The private keys, if specified, must be in wallet
|
||||||
|
// import format (WIF).
|
||||||
|
//
|
||||||
|
// The only input transactions that need to be specified are ones the RPC server
|
||||||
|
// does not already know. This means the list of transaction inputs can be nil
|
||||||
|
// if the RPC server already knows them all.
|
||||||
|
//
|
||||||
|
// NOTE: Unlike the merging functionality of the input transactions, ONLY the
|
||||||
|
// specified private keys will be used, so even if the server already knows some
|
||||||
|
// of the private keys, they will NOT be used. The list of private keys can be
|
||||||
|
// nil in which case any private keys the RPC server knows will be used.
|
||||||
|
//
|
||||||
|
// This function should only used if a non-default signature hash type is
|
||||||
|
// desired. Otherwise, see SignRawTransaction if the RPC server already knows
|
||||||
|
// the input transactions and private keys, SignRawTransaction2 if it already
|
||||||
|
// knows the private keys, or SignRawTransaction3 if it does not know both.
|
||||||
|
func (c *Client) SignRawTransaction4(tx *wire.MsgTx,
|
||||||
|
inputs []btcjson.RawTxInput, privKeysWIF []string,
|
||||||
|
hashType SigHashType) (*wire.MsgTx, bool, error) {
|
||||||
|
|
||||||
|
return c.SignRawTransaction4Async(tx, inputs, privKeysWIF,
|
||||||
|
hashType).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSearchRawTransactionsResult is a future promise to deliver the result
|
||||||
|
// of the SearchRawTransactionsAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureSearchRawTransactionsResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// found raw transactions.
|
||||||
|
func (r FutureSearchRawTransactionsResult) Receive() ([]*wire.MsgTx, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal as an array of strings.
|
||||||
|
var searchRawTxnsResult []string
|
||||||
|
err = json.Unmarshal(res, &searchRawTxnsResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode and deserialize each transaction.
|
||||||
|
msgTxns := make([]*wire.MsgTx, 0, len(searchRawTxnsResult))
|
||||||
|
for _, hexTx := range searchRawTxnsResult {
|
||||||
|
// Decode the serialized transaction hex to raw bytes.
|
||||||
|
serializedTx, err := hex.DecodeString(hexTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize the transaction and add it to the result slice.
|
||||||
|
var msgTx wire.MsgTx
|
||||||
|
err = msgTx.Deserialize(bytes.NewReader(serializedTx))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgTxns = append(msgTxns, &msgTx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgTxns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRawTransactionsAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See SearchRawTransactions for the blocking version and more details.
|
||||||
|
func (c *Client) SearchRawTransactionsAsync(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) FutureSearchRawTransactionsResult {
|
||||||
|
addr := address.EncodeAddress()
|
||||||
|
verbose := btcjson.Int(0)
|
||||||
|
cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count,
|
||||||
|
nil, &reverse, &filterAddrs)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRawTransactions returns transactions that involve the passed address.
|
||||||
|
//
|
||||||
|
// NOTE: Chain servers do not typically provide this capability unless it has
|
||||||
|
// specifically been enabled.
|
||||||
|
//
|
||||||
|
// See SearchRawTransactionsVerbose to retrieve a list of data structures with
|
||||||
|
// information about the transactions instead of the transactions themselves.
|
||||||
|
func (c *Client) SearchRawTransactions(address btcutil.Address, skip, count int, reverse bool, filterAddrs []string) ([]*wire.MsgTx, error) {
|
||||||
|
return c.SearchRawTransactionsAsync(address, skip, count, reverse, filterAddrs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureSearchRawTransactionsVerboseResult is a future promise to deliver the
|
||||||
|
// result of the SearchRawTransactionsVerboseAsync RPC invocation (or an
|
||||||
|
// applicable error).
|
||||||
|
type FutureSearchRawTransactionsVerboseResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns the
|
||||||
|
// found raw transactions.
|
||||||
|
func (r FutureSearchRawTransactionsVerboseResult) Receive() ([]*btcjson.SearchRawTransactionsResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal as an array of raw transaction results.
|
||||||
|
var result []*btcjson.SearchRawTransactionsResult
|
||||||
|
err = json.Unmarshal(res, &result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRawTransactionsVerboseAsync returns an instance of a type that can be
|
||||||
|
// used to get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See SearchRawTransactionsVerbose for the blocking version and more details.
|
||||||
|
func (c *Client) SearchRawTransactionsVerboseAsync(address btcutil.Address, skip,
|
||||||
|
count int, includePrevOut, reverse bool, filterAddrs *[]string) FutureSearchRawTransactionsVerboseResult {
|
||||||
|
|
||||||
|
addr := address.EncodeAddress()
|
||||||
|
verbose := btcjson.Int(1)
|
||||||
|
var prevOut *int
|
||||||
|
if includePrevOut {
|
||||||
|
prevOut = btcjson.Int(1)
|
||||||
|
}
|
||||||
|
cmd := btcjson.NewSearchRawTransactionsCmd(addr, verbose, &skip, &count,
|
||||||
|
prevOut, &reverse, filterAddrs)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRawTransactionsVerbose returns a list of data structures that describe
|
||||||
|
// transactions which involve the passed address.
|
||||||
|
//
|
||||||
|
// NOTE: Chain servers do not typically provide this capability unless it has
|
||||||
|
// specifically been enabled.
|
||||||
|
//
|
||||||
|
// See SearchRawTransactions to retrieve a list of raw transactions instead.
|
||||||
|
func (c *Client) SearchRawTransactionsVerbose(address btcutil.Address, skip,
|
||||||
|
count int, includePrevOut, reverse bool, filterAddrs []string) ([]*btcjson.SearchRawTransactionsResult, error) {
|
||||||
|
|
||||||
|
return c.SearchRawTransactionsVerboseAsync(address, skip, count,
|
||||||
|
includePrevOut, reverse, &filterAddrs).Receive()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FutureDecodeScriptResult is a future promise to deliver the result
|
||||||
|
// of a DecodeScriptAsync RPC invocation (or an applicable error).
|
||||||
|
type FutureDecodeScriptResult chan *response
|
||||||
|
|
||||||
|
// Receive waits for the response promised by the future and returns information
|
||||||
|
// about a script given its serialized bytes.
|
||||||
|
func (r FutureDecodeScriptResult) Receive() (*btcjson.DecodeScriptResult, error) {
|
||||||
|
res, err := receiveFuture(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal result as a decodescript result object.
|
||||||
|
var decodeScriptResult btcjson.DecodeScriptResult
|
||||||
|
err = json.Unmarshal(res, &decodeScriptResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &decodeScriptResult, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeScriptAsync returns an instance of a type that can be used to
|
||||||
|
// get the result of the RPC at some future time by invoking the Receive
|
||||||
|
// function on the returned instance.
|
||||||
|
//
|
||||||
|
// See DecodeScript for the blocking version and more details.
|
||||||
|
func (c *Client) DecodeScriptAsync(serializedScript []byte) FutureDecodeScriptResult {
|
||||||
|
scriptHex := hex.EncodeToString(serializedScript)
|
||||||
|
cmd := btcjson.NewDecodeScriptCmd(scriptHex)
|
||||||
|
return c.sendCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeScript returns information about a script given its serialized bytes.
|
||||||
|
func (c *Client) DecodeScript(serializedScript []byte) (*btcjson.DecodeScriptResult, error) {
|
||||||
|
return c.DecodeScriptAsync(serializedScript).Receive()
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,113 @@
|
||||||
|
wire
|
||||||
|
====
|
||||||
|
|
||||||
|
[](https://travis-ci.org/btcsuite/btcd)
|
||||||
|
[](http://copyfree.org)
|
||||||
|
[](http://godoc.org/github.com/btcsuite/btcd/wire)
|
||||||
|
=======
|
||||||
|
|
||||||
|
Package wire implements the bitcoin wire protocol. A comprehensive suite of
|
||||||
|
tests with 100% test coverage is provided to ensure proper functionality.
|
||||||
|
|
||||||
|
There is an associated blog post about the release of this package
|
||||||
|
[here](https://blog.conformal.com/btcwire-the-bitcoin-wire-protocol-package-from-btcd/).
|
||||||
|
|
||||||
|
This package has intentionally been designed so it can be used as a standalone
|
||||||
|
package for any projects needing to interface with bitcoin peers at the wire
|
||||||
|
protocol level.
|
||||||
|
|
||||||
|
## Installation and Updating
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go get -u github.com/btcsuite/btcd/wire
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bitcoin Message Overview
|
||||||
|
|
||||||
|
The bitcoin protocol consists of exchanging messages between peers. Each message
|
||||||
|
is preceded by a header which identifies information about it such as which
|
||||||
|
bitcoin network it is a part of, its type, how big it is, and a checksum to
|
||||||
|
verify validity. All encoding and decoding of message headers is handled by this
|
||||||
|
package.
|
||||||
|
|
||||||
|
To accomplish this, there is a generic interface for bitcoin messages named
|
||||||
|
`Message` which allows messages of any type to be read, written, or passed
|
||||||
|
around through channels, functions, etc. In addition, concrete implementations
|
||||||
|
of most of the currently supported bitcoin messages are provided. For these
|
||||||
|
supported messages, all of the details of marshalling and unmarshalling to and
|
||||||
|
from the wire using bitcoin encoding are handled so the caller doesn't have to
|
||||||
|
concern themselves with the specifics.
|
||||||
|
|
||||||
|
## Reading Messages Example
|
||||||
|
|
||||||
|
In order to unmarshal bitcoin messages from the wire, use the `ReadMessage`
|
||||||
|
function. It accepts any `io.Reader`, but typically this will be a `net.Conn`
|
||||||
|
to a remote node running a bitcoin peer. Example syntax is:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
// Use the most recent protocol version supported by the package and the
|
||||||
|
// main bitcoin network.
|
||||||
|
pver := wire.ProtocolVersion
|
||||||
|
btcnet := wire.MainNet
|
||||||
|
|
||||||
|
// Reads and validates the next bitcoin message from conn using the
|
||||||
|
// protocol version pver and the bitcoin network btcnet. The returns
|
||||||
|
// are a wire.Message, a []byte which contains the unmarshalled
|
||||||
|
// raw payload, and a possible error.
|
||||||
|
msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet)
|
||||||
|
if err != nil {
|
||||||
|
// Log and handle the error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the package documentation for details on determining the message type.
|
||||||
|
|
||||||
|
## Writing Messages Example
|
||||||
|
|
||||||
|
In order to marshal bitcoin messages to the wire, use the `WriteMessage`
|
||||||
|
function. It accepts any `io.Writer`, but typically this will be a `net.Conn`
|
||||||
|
to a remote node running a bitcoin peer. Example syntax to request addresses
|
||||||
|
from a remote peer is:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
// Use the most recent protocol version supported by the package and the
|
||||||
|
// main bitcoin network.
|
||||||
|
pver := wire.ProtocolVersion
|
||||||
|
btcnet := wire.MainNet
|
||||||
|
|
||||||
|
// Create a new getaddr bitcoin message.
|
||||||
|
msg := wire.NewMsgGetAddr()
|
||||||
|
|
||||||
|
// Writes a bitcoin message msg to conn using the protocol version
|
||||||
|
// pver, and the bitcoin network btcnet. The return is a possible
|
||||||
|
// error.
|
||||||
|
err := wire.WriteMessage(conn, msg, pver, btcnet)
|
||||||
|
if err != nil {
|
||||||
|
// Log and handle the error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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 wire is licensed under the [copyfree](http://copyfree.org) ISC
|
||||||
|
License.
|
|
@ -0,0 +1,128 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxBlockHeaderPayload is the maximum number of bytes a block header can be.
|
||||||
|
// Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes +
|
||||||
|
// PrevBlock and MerkleRoot hashes.
|
||||||
|
const MaxBlockHeaderPayload = 16 + (chainhash.HashSize * 2)
|
||||||
|
|
||||||
|
// BlockHeader defines information about a block and is used in the bitcoin
|
||||||
|
// block (MsgBlock) and headers (MsgHeaders) messages.
|
||||||
|
type BlockHeader struct {
|
||||||
|
// Version of the block. This is not the same as the protocol version.
|
||||||
|
Version int32
|
||||||
|
|
||||||
|
// Hash of the previous block header in the block chain.
|
||||||
|
PrevBlock chainhash.Hash
|
||||||
|
|
||||||
|
// Merkle tree reference to hash of all transactions for the block.
|
||||||
|
MerkleRoot chainhash.Hash
|
||||||
|
|
||||||
|
// Time the block was created. This is, unfortunately, encoded as a
|
||||||
|
// uint32 on the wire and therefore is limited to 2106.
|
||||||
|
Timestamp time.Time
|
||||||
|
|
||||||
|
// Difficulty target for the block.
|
||||||
|
Bits uint32
|
||||||
|
|
||||||
|
// Nonce used to generate the block.
|
||||||
|
Nonce uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockHeaderLen is a constant that represents the number of bytes for a block
|
||||||
|
// header.
|
||||||
|
const blockHeaderLen = 80
|
||||||
|
|
||||||
|
// BlockHash computes the block identifier hash for the given block header.
|
||||||
|
func (h *BlockHeader) BlockHash() chainhash.Hash {
|
||||||
|
// Encode the header and double sha256 everything prior to the number of
|
||||||
|
// transactions. Ignore the error returns since there is no way the
|
||||||
|
// encode could fail except being out of memory which would cause a
|
||||||
|
// run-time panic.
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload))
|
||||||
|
_ = writeBlockHeader(buf, 0, h)
|
||||||
|
|
||||||
|
return chainhash.DoubleHashH(buf.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Deserialize for decoding block headers stored to disk, such as in a
|
||||||
|
// database, as opposed to decoding block headers from the wire.
|
||||||
|
func (h *BlockHeader) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
return readBlockHeader(r, pver, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Serialize for encoding block headers to be stored to disk, such as in a
|
||||||
|
// database, as opposed to encoding block headers for the wire.
|
||||||
|
func (h *BlockHeader) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
return writeBlockHeader(w, pver, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a block header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database while respecting
|
||||||
|
// the Version field.
|
||||||
|
func (h *BlockHeader) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of readBlockHeader.
|
||||||
|
return readBlockHeader(r, 0, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes a block header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database while respecting
|
||||||
|
// the Version field.
|
||||||
|
func (h *BlockHeader) Serialize(w io.Writer) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of writeBlockHeader.
|
||||||
|
return writeBlockHeader(w, 0, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockHeader returns a new BlockHeader using the provided version, previous
|
||||||
|
// block hash, merkle root hash, difficulty bits, and nonce used to generate the
|
||||||
|
// block with defaults for the remaining fields.
|
||||||
|
func NewBlockHeader(version int32, prevHash, merkleRootHash *chainhash.Hash,
|
||||||
|
bits uint32, nonce uint32) *BlockHeader {
|
||||||
|
|
||||||
|
// Limit the timestamp to one second precision since the protocol
|
||||||
|
// doesn't support better.
|
||||||
|
return &BlockHeader{
|
||||||
|
Version: version,
|
||||||
|
PrevBlock: *prevHash,
|
||||||
|
MerkleRoot: *merkleRootHash,
|
||||||
|
Timestamp: time.Unix(time.Now().Unix(), 0),
|
||||||
|
Bits: bits,
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readBlockHeader reads a bitcoin block header from r. See Deserialize for
|
||||||
|
// decoding block headers stored to disk, such as in a database, as opposed to
|
||||||
|
// decoding from the wire.
|
||||||
|
func readBlockHeader(r io.Reader, pver uint32, bh *BlockHeader) error {
|
||||||
|
return readElements(r, &bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
|
||||||
|
(*uint32Time)(&bh.Timestamp), &bh.Bits, &bh.Nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBlockHeader writes a bitcoin block header to w. See Serialize for
|
||||||
|
// encoding block headers to be stored to disk, such as in a database, as
|
||||||
|
// opposed to encoding for the wire.
|
||||||
|
func writeBlockHeader(w io.Writer, pver uint32, bh *BlockHeader) error {
|
||||||
|
sec := uint32(bh.Timestamp.Unix())
|
||||||
|
return writeElements(w, bh.Version, &bh.PrevBlock, &bh.MerkleRoot,
|
||||||
|
sec, bh.Bits, bh.Nonce)
|
||||||
|
}
|
|
@ -0,0 +1,689 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxVarIntPayload is the maximum payload size for a variable length integer.
|
||||||
|
MaxVarIntPayload = 9
|
||||||
|
|
||||||
|
// binaryFreeListMaxItems is the number of buffers to keep in the free
|
||||||
|
// list to use for binary serialization and deserialization.
|
||||||
|
binaryFreeListMaxItems = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// littleEndian is a convenience variable since binary.LittleEndian is
|
||||||
|
// quite long.
|
||||||
|
littleEndian = binary.LittleEndian
|
||||||
|
|
||||||
|
// bigEndian is a convenience variable since binary.BigEndian is quite
|
||||||
|
// long.
|
||||||
|
bigEndian = binary.BigEndian
|
||||||
|
)
|
||||||
|
|
||||||
|
// binaryFreeList defines a concurrent safe free list of byte slices (up to the
|
||||||
|
// maximum number defined by the binaryFreeListMaxItems constant) that have a
|
||||||
|
// cap of 8 (thus it supports up to a uint64). It is used to provide temporary
|
||||||
|
// buffers for serializing and deserializing primitive numbers to and from their
|
||||||
|
// binary encoding in order to greatly reduce the number of allocations
|
||||||
|
// required.
|
||||||
|
//
|
||||||
|
// For convenience, functions are provided for each of the primitive unsigned
|
||||||
|
// integers that automatically obtain a buffer from the free list, perform the
|
||||||
|
// necessary binary conversion, read from or write to the given io.Reader or
|
||||||
|
// io.Writer, and return the buffer to the free list.
|
||||||
|
type binaryFreeList chan []byte
|
||||||
|
|
||||||
|
// Borrow returns a byte slice from the free list with a length of 8. A new
|
||||||
|
// buffer is allocated if there are not any available on the free list.
|
||||||
|
func (l binaryFreeList) Borrow() []byte {
|
||||||
|
var buf []byte
|
||||||
|
select {
|
||||||
|
case buf = <-l:
|
||||||
|
default:
|
||||||
|
buf = make([]byte, 8)
|
||||||
|
}
|
||||||
|
return buf[:8]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return puts the provided byte slice back on the free list. The buffer MUST
|
||||||
|
// have been obtained via the Borrow function and therefore have a cap of 8.
|
||||||
|
func (l binaryFreeList) Return(buf []byte) {
|
||||||
|
select {
|
||||||
|
case l <- buf:
|
||||||
|
default:
|
||||||
|
// Let it go to the garbage collector.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint8 reads a single byte from the provided reader using a buffer from the
|
||||||
|
// free list and returns it as a uint8.
|
||||||
|
func (l binaryFreeList) Uint8(r io.Reader) (uint8, error) {
|
||||||
|
buf := l.Borrow()[:1]
|
||||||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||||||
|
l.Return(buf)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv := buf[0]
|
||||||
|
l.Return(buf)
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint16 reads two bytes from the provided reader using a buffer from the
|
||||||
|
// free list, converts it to a number using the provided byte order, and returns
|
||||||
|
// the resulting uint16.
|
||||||
|
func (l binaryFreeList) Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
|
||||||
|
buf := l.Borrow()[:2]
|
||||||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||||||
|
l.Return(buf)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv := byteOrder.Uint16(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 reads four bytes from the provided reader using a buffer from the
|
||||||
|
// free list, converts it to a number using the provided byte order, and returns
|
||||||
|
// the resulting uint32.
|
||||||
|
func (l binaryFreeList) Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
|
||||||
|
buf := l.Borrow()[:4]
|
||||||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||||||
|
l.Return(buf)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv := byteOrder.Uint32(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 reads eight bytes from the provided reader using a buffer from the
|
||||||
|
// free list, converts it to a number using the provided byte order, and returns
|
||||||
|
// the resulting uint64.
|
||||||
|
func (l binaryFreeList) Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
|
||||||
|
buf := l.Borrow()[:8]
|
||||||
|
if _, err := io.ReadFull(r, buf); err != nil {
|
||||||
|
l.Return(buf)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv := byteOrder.Uint64(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutUint8 copies the provided uint8 into a buffer from the free list and
|
||||||
|
// writes the resulting byte to the given writer.
|
||||||
|
func (l binaryFreeList) PutUint8(w io.Writer, val uint8) error {
|
||||||
|
buf := l.Borrow()[:1]
|
||||||
|
buf[0] = val
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutUint16 serializes the provided uint16 using the given byte order into a
|
||||||
|
// buffer from the free list and writes the resulting two bytes to the given
|
||||||
|
// writer.
|
||||||
|
func (l binaryFreeList) PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
|
||||||
|
buf := l.Borrow()[:2]
|
||||||
|
byteOrder.PutUint16(buf, val)
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutUint32 serializes the provided uint32 using the given byte order into a
|
||||||
|
// buffer from the free list and writes the resulting four bytes to the given
|
||||||
|
// writer.
|
||||||
|
func (l binaryFreeList) PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
|
||||||
|
buf := l.Borrow()[:4]
|
||||||
|
byteOrder.PutUint32(buf, val)
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutUint64 serializes the provided uint64 using the given byte order into a
|
||||||
|
// buffer from the free list and writes the resulting eight bytes to the given
|
||||||
|
// writer.
|
||||||
|
func (l binaryFreeList) PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
|
||||||
|
buf := l.Borrow()[:8]
|
||||||
|
byteOrder.PutUint64(buf, val)
|
||||||
|
_, err := w.Write(buf)
|
||||||
|
l.Return(buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// binarySerializer provides a free list of buffers to use for serializing and
|
||||||
|
// deserializing primitive integer values to and from io.Readers and io.Writers.
|
||||||
|
var binarySerializer binaryFreeList = make(chan []byte, binaryFreeListMaxItems)
|
||||||
|
|
||||||
|
// errNonCanonicalVarInt is the common format string used for non-canonically
|
||||||
|
// encoded variable length integer errors.
|
||||||
|
var errNonCanonicalVarInt = "non-canonical varint %x - discriminant %x must " +
|
||||||
|
"encode a value greater than %x"
|
||||||
|
|
||||||
|
// uint32Time represents a unix timestamp encoded with a uint32. It is used as
|
||||||
|
// a way to signal the readElement function how to decode a timestamp into a Go
|
||||||
|
// time.Time since it is otherwise ambiguous.
|
||||||
|
type uint32Time time.Time
|
||||||
|
|
||||||
|
// int64Time represents a unix timestamp encoded with an int64. It is used as
|
||||||
|
// a way to signal the readElement function how to decode a timestamp into a Go
|
||||||
|
// time.Time since it is otherwise ambiguous.
|
||||||
|
type int64Time time.Time
|
||||||
|
|
||||||
|
// readElement reads the next sequence of bytes from r using little endian
|
||||||
|
// depending on the concrete type of element pointed to.
|
||||||
|
func readElement(r io.Reader, element interface{}) error {
|
||||||
|
// Attempt to read the element based on the concrete type via fast
|
||||||
|
// type assertions first.
|
||||||
|
switch e := element.(type) {
|
||||||
|
case *int32:
|
||||||
|
rv, err := binarySerializer.Uint32(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = int32(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *uint32:
|
||||||
|
rv, err := binarySerializer.Uint32(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = rv
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *int64:
|
||||||
|
rv, err := binarySerializer.Uint64(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = int64(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *uint64:
|
||||||
|
rv, err := binarySerializer.Uint64(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = rv
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *bool:
|
||||||
|
rv, err := binarySerializer.Uint8(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rv == 0x00 {
|
||||||
|
*e = false
|
||||||
|
} else {
|
||||||
|
*e = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Unix timestamp encoded as a uint32.
|
||||||
|
case *uint32Time:
|
||||||
|
rv, err := binarySerializer.Uint32(r, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = uint32Time(time.Unix(int64(rv), 0))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Unix timestamp encoded as an int64.
|
||||||
|
case *int64Time:
|
||||||
|
rv, err := binarySerializer.Uint64(r, binary.LittleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = int64Time(time.Unix(int64(rv), 0))
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Message header checksum.
|
||||||
|
case *[4]byte:
|
||||||
|
_, err := io.ReadFull(r, e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Message header command.
|
||||||
|
case *[CommandSize]uint8:
|
||||||
|
_, err := io.ReadFull(r, e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// IP address.
|
||||||
|
case *[16]byte:
|
||||||
|
_, err := io.ReadFull(r, e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *chainhash.Hash:
|
||||||
|
_, err := io.ReadFull(r, e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *ServiceFlag:
|
||||||
|
rv, err := binarySerializer.Uint64(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = ServiceFlag(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *InvType:
|
||||||
|
rv, err := binarySerializer.Uint32(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = InvType(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *BitcoinNet:
|
||||||
|
rv, err := binarySerializer.Uint32(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = BitcoinNet(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *BloomUpdateType:
|
||||||
|
rv, err := binarySerializer.Uint8(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = BloomUpdateType(rv)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *RejectCode:
|
||||||
|
rv, err := binarySerializer.Uint8(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*e = RejectCode(rv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the slower binary.Read if a fast path was not available
|
||||||
|
// above.
|
||||||
|
return binary.Read(r, littleEndian, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readElements reads multiple items from r. It is equivalent to multiple
|
||||||
|
// calls to readElement.
|
||||||
|
func readElements(r io.Reader, elements ...interface{}) error {
|
||||||
|
for _, element := range elements {
|
||||||
|
err := readElement(r, element)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeElement writes the little endian representation of element to w.
|
||||||
|
func writeElement(w io.Writer, element interface{}) error {
|
||||||
|
// Attempt to write the element based on the concrete type via fast
|
||||||
|
// type assertions first.
|
||||||
|
switch e := element.(type) {
|
||||||
|
case int32:
|
||||||
|
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case uint32:
|
||||||
|
err := binarySerializer.PutUint32(w, littleEndian, e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
err := binarySerializer.PutUint64(w, littleEndian, uint64(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
err := binarySerializer.PutUint64(w, littleEndian, e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case bool:
|
||||||
|
var err error
|
||||||
|
if e {
|
||||||
|
err = binarySerializer.PutUint8(w, 0x01)
|
||||||
|
} else {
|
||||||
|
err = binarySerializer.PutUint8(w, 0x00)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Message header checksum.
|
||||||
|
case [4]byte:
|
||||||
|
_, err := w.Write(e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// Message header command.
|
||||||
|
case [CommandSize]uint8:
|
||||||
|
_, err := w.Write(e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
// IP address.
|
||||||
|
case [16]byte:
|
||||||
|
_, err := w.Write(e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case *chainhash.Hash:
|
||||||
|
_, err := w.Write(e[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case ServiceFlag:
|
||||||
|
err := binarySerializer.PutUint64(w, littleEndian, uint64(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case InvType:
|
||||||
|
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case BitcoinNet:
|
||||||
|
err := binarySerializer.PutUint32(w, littleEndian, uint32(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case BloomUpdateType:
|
||||||
|
err := binarySerializer.PutUint8(w, uint8(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case RejectCode:
|
||||||
|
err := binarySerializer.PutUint8(w, uint8(e))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the slower binary.Write if a fast path was not available
|
||||||
|
// above.
|
||||||
|
return binary.Write(w, littleEndian, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeElements writes multiple items to w. It is equivalent to multiple
|
||||||
|
// calls to writeElement.
|
||||||
|
func writeElements(w io.Writer, elements ...interface{}) error {
|
||||||
|
for _, element := range elements {
|
||||||
|
err := writeElement(w, element)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadVarInt reads a variable length integer from r and returns it as a uint64.
|
||||||
|
func ReadVarInt(r io.Reader, pver uint32) (uint64, error) {
|
||||||
|
discriminant, err := binarySerializer.Uint8(r)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rv uint64
|
||||||
|
switch discriminant {
|
||||||
|
case 0xff:
|
||||||
|
sv, err := binarySerializer.Uint64(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv = sv
|
||||||
|
|
||||||
|
// The encoding is not canonical if the value could have been
|
||||||
|
// encoded using fewer bytes.
|
||||||
|
min := uint64(0x100000000)
|
||||||
|
if rv < min {
|
||||||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||||||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0xfe:
|
||||||
|
sv, err := binarySerializer.Uint32(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv = uint64(sv)
|
||||||
|
|
||||||
|
// The encoding is not canonical if the value could have been
|
||||||
|
// encoded using fewer bytes.
|
||||||
|
min := uint64(0x10000)
|
||||||
|
if rv < min {
|
||||||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||||||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0xfd:
|
||||||
|
sv, err := binarySerializer.Uint16(r, littleEndian)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rv = uint64(sv)
|
||||||
|
|
||||||
|
// The encoding is not canonical if the value could have been
|
||||||
|
// encoded using fewer bytes.
|
||||||
|
min := uint64(0xfd)
|
||||||
|
if rv < min {
|
||||||
|
return 0, messageError("ReadVarInt", fmt.Sprintf(
|
||||||
|
errNonCanonicalVarInt, rv, discriminant, min))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
rv = uint64(discriminant)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteVarInt serializes val to w using a variable number of bytes depending
|
||||||
|
// on its value.
|
||||||
|
func WriteVarInt(w io.Writer, pver uint32, val uint64) error {
|
||||||
|
if val < 0xfd {
|
||||||
|
return binarySerializer.PutUint8(w, uint8(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
if val <= math.MaxUint16 {
|
||||||
|
err := binarySerializer.PutUint8(w, 0xfd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binarySerializer.PutUint16(w, littleEndian, uint16(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
if val <= math.MaxUint32 {
|
||||||
|
err := binarySerializer.PutUint8(w, 0xfe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binarySerializer.PutUint32(w, littleEndian, uint32(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binarySerializer.PutUint8(w, 0xff)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binarySerializer.PutUint64(w, littleEndian, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarIntSerializeSize returns the number of bytes it would take to serialize
|
||||||
|
// val as a variable length integer.
|
||||||
|
func VarIntSerializeSize(val uint64) int {
|
||||||
|
// The value is small enough to be represented by itself, so it's
|
||||||
|
// just 1 byte.
|
||||||
|
if val < 0xfd {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discriminant 1 byte plus 2 bytes for the uint16.
|
||||||
|
if val <= math.MaxUint16 {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discriminant 1 byte plus 4 bytes for the uint32.
|
||||||
|
if val <= math.MaxUint32 {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discriminant 1 byte plus 8 bytes for the uint64.
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadVarString reads a variable length string from r and returns it as a Go
|
||||||
|
// string. A variable length string is encoded as a variable length integer
|
||||||
|
// containing the length of the string followed by the bytes that represent the
|
||||||
|
// string itself. An error is returned if the length is greater than the
|
||||||
|
// maximum block payload size since it helps protect against memory exhaustion
|
||||||
|
// attacks and forced panics through malformed messages.
|
||||||
|
func ReadVarString(r io.Reader, pver uint32) (string, error) {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent variable length strings that are larger than the maximum
|
||||||
|
// message size. It would be possible to cause memory exhaustion and
|
||||||
|
// panics without a sane upper bound on this count.
|
||||||
|
if count > MaxMessagePayload {
|
||||||
|
str := fmt.Sprintf("variable length string is too long "+
|
||||||
|
"[count %d, max %d]", count, MaxMessagePayload)
|
||||||
|
return "", messageError("ReadVarString", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, count)
|
||||||
|
_, err = io.ReadFull(r, buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteVarString serializes str to w as a variable length integer containing
|
||||||
|
// the length of the string followed by the bytes that represent the string
|
||||||
|
// itself.
|
||||||
|
func WriteVarString(w io.Writer, pver uint32, str string) error {
|
||||||
|
err := WriteVarInt(w, pver, uint64(len(str)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = w.Write([]byte(str))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadVarBytes reads a variable length byte array. A byte array is encoded
|
||||||
|
// as a varInt containing the length of the array followed by the bytes
|
||||||
|
// themselves. An error is returned if the length is greater than the
|
||||||
|
// passed maxAllowed parameter which helps protect against memory exhaustion
|
||||||
|
// attacks and forced panics through malformed messages. The fieldName
|
||||||
|
// parameter is only used for the error message so it provides more context in
|
||||||
|
// the error.
|
||||||
|
func ReadVarBytes(r io.Reader, pver uint32, maxAllowed uint32,
|
||||||
|
fieldName string) ([]byte, error) {
|
||||||
|
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent byte array larger than the max message size. It would
|
||||||
|
// be possible to cause memory exhaustion and panics without a sane
|
||||||
|
// upper bound on this count.
|
||||||
|
if count > uint64(maxAllowed) {
|
||||||
|
str := fmt.Sprintf("%s is larger than the max allowed size "+
|
||||||
|
"[count %d, max %d]", fieldName, count, maxAllowed)
|
||||||
|
return nil, messageError("ReadVarBytes", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, count)
|
||||||
|
_, err = io.ReadFull(r, b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteVarBytes serializes a variable length byte array to w as a varInt
|
||||||
|
// containing the number of bytes, followed by the bytes themselves.
|
||||||
|
func WriteVarBytes(w io.Writer, pver uint32, bytes []byte) error {
|
||||||
|
slen := uint64(len(bytes))
|
||||||
|
err := WriteVarInt(w, pver, slen)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(bytes)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomUint64 returns a cryptographically random uint64 value. This
|
||||||
|
// unexported version takes a reader primarily to ensure the error paths
|
||||||
|
// can be properly tested by passing a fake reader in the tests.
|
||||||
|
func randomUint64(r io.Reader) (uint64, error) {
|
||||||
|
rv, err := binarySerializer.Uint64(r, bigEndian)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RandomUint64 returns a cryptographically random uint64 value.
|
||||||
|
func RandomUint64() (uint64, error) {
|
||||||
|
return randomUint64(rand.Reader)
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
// 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 wire implements the bitcoin wire protocol.
|
||||||
|
|
||||||
|
For the complete details of the bitcoin protocol, see the official wiki entry
|
||||||
|
at https://en.bitcoin.it/wiki/Protocol_specification. The following only serves
|
||||||
|
as a quick overview to provide information on how to use the package.
|
||||||
|
|
||||||
|
At a high level, this package provides support for marshalling and unmarshalling
|
||||||
|
supported bitcoin messages to and from the wire. This package does not deal
|
||||||
|
with the specifics of message handling such as what to do when a message is
|
||||||
|
received. This provides the caller with a high level of flexibility.
|
||||||
|
|
||||||
|
Bitcoin Message Overview
|
||||||
|
|
||||||
|
The bitcoin protocol consists of exchanging messages between peers. Each
|
||||||
|
message is preceded by a header which identifies information about it such as
|
||||||
|
which bitcoin network it is a part of, its type, how big it is, and a checksum
|
||||||
|
to verify validity. All encoding and decoding of message headers is handled by
|
||||||
|
this package.
|
||||||
|
|
||||||
|
To accomplish this, there is a generic interface for bitcoin messages named
|
||||||
|
Message which allows messages of any type to be read, written, or passed around
|
||||||
|
through channels, functions, etc. In addition, concrete implementations of most
|
||||||
|
of the currently supported bitcoin messages are provided. For these supported
|
||||||
|
messages, all of the details of marshalling and unmarshalling to and from the
|
||||||
|
wire using bitcoin encoding are handled so the caller doesn't have to concern
|
||||||
|
themselves with the specifics.
|
||||||
|
|
||||||
|
Message Interaction
|
||||||
|
|
||||||
|
The following provides a quick summary of how the bitcoin messages are intended
|
||||||
|
to interact with one another. As stated above, these interactions are not
|
||||||
|
directly handled by this package. For more in-depth details about the
|
||||||
|
appropriate interactions, see the official bitcoin protocol wiki entry at
|
||||||
|
https://en.bitcoin.it/wiki/Protocol_specification.
|
||||||
|
|
||||||
|
The initial handshake consists of two peers sending each other a version message
|
||||||
|
(MsgVersion) followed by responding with a verack message (MsgVerAck). Both
|
||||||
|
peers use the information in the version message (MsgVersion) to negotiate
|
||||||
|
things such as protocol version and supported services with each other. Once
|
||||||
|
the initial handshake is complete, the following chart indicates message
|
||||||
|
interactions in no particular order.
|
||||||
|
|
||||||
|
Peer A Sends Peer B Responds
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
getaddr message (MsgGetAddr) addr message (MsgAddr)
|
||||||
|
getblocks message (MsgGetBlocks) inv message (MsgInv)
|
||||||
|
inv message (MsgInv) getdata message (MsgGetData)
|
||||||
|
getdata message (MsgGetData) block message (MsgBlock) -or-
|
||||||
|
tx message (MsgTx) -or-
|
||||||
|
notfound message (MsgNotFound)
|
||||||
|
getheaders message (MsgGetHeaders) headers message (MsgHeaders)
|
||||||
|
ping message (MsgPing) pong message (MsgHeaders)* -or-
|
||||||
|
(none -- Ability to send message is enough)
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
* The pong message was not added until later protocol versions as defined
|
||||||
|
in BIP0031. The BIP0031Version constant can be used to detect a recent
|
||||||
|
enough protocol version for this purpose (version > BIP0031Version).
|
||||||
|
|
||||||
|
Common Parameters
|
||||||
|
|
||||||
|
There are several common parameters that arise when using this package to read
|
||||||
|
and write bitcoin messages. The following sections provide a quick overview of
|
||||||
|
these parameters so the next sections can build on them.
|
||||||
|
|
||||||
|
Protocol Version
|
||||||
|
|
||||||
|
The protocol version should be negotiated with the remote peer at a higher
|
||||||
|
level than this package via the version (MsgVersion) message exchange, however,
|
||||||
|
this package provides the wire.ProtocolVersion constant which indicates the
|
||||||
|
latest protocol version this package supports and is typically the value to use
|
||||||
|
for all outbound connections before a potentially lower protocol version is
|
||||||
|
negotiated.
|
||||||
|
|
||||||
|
Bitcoin Network
|
||||||
|
|
||||||
|
The bitcoin network is a magic number which is used to identify the start of a
|
||||||
|
message and which bitcoin network the message applies to. This package provides
|
||||||
|
the following constants:
|
||||||
|
|
||||||
|
wire.MainNet
|
||||||
|
wire.TestNet (Regression test network)
|
||||||
|
wire.TestNet3 (Test network version 3)
|
||||||
|
wire.SimNet (Simulation test network)
|
||||||
|
|
||||||
|
Determining Message Type
|
||||||
|
|
||||||
|
As discussed in the bitcoin message overview section, this package reads
|
||||||
|
and writes bitcoin messages using a generic interface named Message. In
|
||||||
|
order to determine the actual concrete type of the message, use a type
|
||||||
|
switch or type assertion. An example of a type switch follows:
|
||||||
|
|
||||||
|
// Assumes msg is already a valid concrete message such as one created
|
||||||
|
// via NewMsgVersion or read via ReadMessage.
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case *wire.MsgVersion:
|
||||||
|
// The message is a pointer to a MsgVersion struct.
|
||||||
|
fmt.Printf("Protocol version: %v", msg.ProtocolVersion)
|
||||||
|
case *wire.MsgBlock:
|
||||||
|
// The message is a pointer to a MsgBlock struct.
|
||||||
|
fmt.Printf("Number of tx in block: %v", msg.Header.TxnCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
Reading Messages
|
||||||
|
|
||||||
|
In order to unmarshall bitcoin messages from the wire, use the ReadMessage
|
||||||
|
function. It accepts any io.Reader, but typically this will be a net.Conn to
|
||||||
|
a remote node running a bitcoin peer. Example syntax is:
|
||||||
|
|
||||||
|
// Reads and validates the next bitcoin message from conn using the
|
||||||
|
// protocol version pver and the bitcoin network btcnet. The returns
|
||||||
|
// are a wire.Message, a []byte which contains the unmarshalled
|
||||||
|
// raw payload, and a possible error.
|
||||||
|
msg, rawPayload, err := wire.ReadMessage(conn, pver, btcnet)
|
||||||
|
if err != nil {
|
||||||
|
// Log and handle the error
|
||||||
|
}
|
||||||
|
|
||||||
|
Writing Messages
|
||||||
|
|
||||||
|
In order to marshall bitcoin messages to the wire, use the WriteMessage
|
||||||
|
function. It accepts any io.Writer, but typically this will be a net.Conn to
|
||||||
|
a remote node running a bitcoin peer. Example syntax to request addresses
|
||||||
|
from a remote peer is:
|
||||||
|
|
||||||
|
// Create a new getaddr bitcoin message.
|
||||||
|
msg := wire.NewMsgGetAddr()
|
||||||
|
|
||||||
|
// Writes a bitcoin message msg to conn using the protocol version
|
||||||
|
// pver, and the bitcoin network btcnet. The return is a possible
|
||||||
|
// error.
|
||||||
|
err := wire.WriteMessage(conn, msg, pver, btcnet)
|
||||||
|
if err != nil {
|
||||||
|
// Log and handle the error
|
||||||
|
}
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
Errors returned by this package are either the raw errors provided by underlying
|
||||||
|
calls to read/write from streams such as io.EOF, io.ErrUnexpectedEOF, and
|
||||||
|
io.ErrShortWrite, or of type wire.MessageError. This allows the caller to
|
||||||
|
differentiate between general IO errors and malformed messages through type
|
||||||
|
assertions.
|
||||||
|
|
||||||
|
Bitcoin Improvement Proposals
|
||||||
|
|
||||||
|
This package includes spec changes outlined by the following BIPs:
|
||||||
|
|
||||||
|
BIP0014 (https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki)
|
||||||
|
BIP0031 (https://github.com/bitcoin/bips/blob/master/bip-0031.mediawiki)
|
||||||
|
BIP0035 (https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki)
|
||||||
|
BIP0037 (https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki)
|
||||||
|
BIP0111 (https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki)
|
||||||
|
BIP0130 (https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki)
|
||||||
|
BIP0133 (https://github.com/bitcoin/bips/blob/master/bip-0133.mediawiki)
|
||||||
|
*/
|
||||||
|
package wire
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageError describes an issue with a message.
|
||||||
|
// An example of some potential issues are messages from the wrong bitcoin
|
||||||
|
// network, invalid commands, mismatched checksums, and exceeding max payloads.
|
||||||
|
//
|
||||||
|
// This provides a mechanism for the caller to type assert the error to
|
||||||
|
// differentiate between general io errors such as io.EOF and issues that
|
||||||
|
// resulted from malformed messages.
|
||||||
|
type MessageError struct {
|
||||||
|
Func string // Function name
|
||||||
|
Description string // Human readable description of the issue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error satisfies the error interface and prints human-readable errors.
|
||||||
|
func (e *MessageError) Error() string {
|
||||||
|
if e.Func != "" {
|
||||||
|
return fmt.Sprintf("%v: %v", e.Func, e.Description)
|
||||||
|
}
|
||||||
|
return e.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageError creates an error for the given function and description.
|
||||||
|
func messageError(f string, desc string) *MessageError {
|
||||||
|
return &MessageError{Func: f, Description: desc}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxInvPerMsg is the maximum number of inventory vectors that can be in a
|
||||||
|
// single bitcoin inv message.
|
||||||
|
MaxInvPerMsg = 50000
|
||||||
|
|
||||||
|
// Maximum payload size for an inventory vector.
|
||||||
|
maxInvVectPayload = 4 + chainhash.HashSize
|
||||||
|
|
||||||
|
// InvWitnessFlag denotes that the inventory vector type is requesting,
|
||||||
|
// or sending a version which includes witness data.
|
||||||
|
InvWitnessFlag = 1 << 30
|
||||||
|
)
|
||||||
|
|
||||||
|
// InvType represents the allowed types of inventory vectors. See InvVect.
|
||||||
|
type InvType uint32
|
||||||
|
|
||||||
|
// These constants define the various supported inventory vector types.
|
||||||
|
const (
|
||||||
|
InvTypeError InvType = 0
|
||||||
|
InvTypeTx InvType = 1
|
||||||
|
InvTypeBlock InvType = 2
|
||||||
|
InvTypeFilteredBlock InvType = 3
|
||||||
|
InvTypeWitnessBlock InvType = InvTypeBlock | InvWitnessFlag
|
||||||
|
InvTypeWitnessTx InvType = InvTypeTx | InvWitnessFlag
|
||||||
|
InvTypeFilteredWitnessBlock InvType = InvTypeFilteredBlock | InvWitnessFlag
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map of service flags back to their constant names for pretty printing.
|
||||||
|
var ivStrings = map[InvType]string{
|
||||||
|
InvTypeError: "ERROR",
|
||||||
|
InvTypeTx: "MSG_TX",
|
||||||
|
InvTypeBlock: "MSG_BLOCK",
|
||||||
|
InvTypeFilteredBlock: "MSG_FILTERED_BLOCK",
|
||||||
|
InvTypeWitnessBlock: "MSG_WITNESS_BLOCK",
|
||||||
|
InvTypeWitnessTx: "MSG_WITNESS_TX",
|
||||||
|
InvTypeFilteredWitnessBlock: "MSG_FILTERED_WITNESS_BLOCK",
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the InvType in human-readable form.
|
||||||
|
func (invtype InvType) String() string {
|
||||||
|
if s, ok := ivStrings[invtype]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Unknown InvType (%d)", uint32(invtype))
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvVect defines a bitcoin inventory vector which is used to describe data,
|
||||||
|
// as specified by the Type field, that a peer wants, has, or does not have to
|
||||||
|
// another peer.
|
||||||
|
type InvVect struct {
|
||||||
|
Type InvType // Type of data
|
||||||
|
Hash chainhash.Hash // Hash of the data
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInvVect returns a new InvVect using the provided type and hash.
|
||||||
|
func NewInvVect(typ InvType, hash *chainhash.Hash) *InvVect {
|
||||||
|
return &InvVect{
|
||||||
|
Type: typ,
|
||||||
|
Hash: *hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readInvVect reads an encoded InvVect from r depending on the protocol
|
||||||
|
// version.
|
||||||
|
func readInvVect(r io.Reader, pver uint32, iv *InvVect) error {
|
||||||
|
return readElements(r, &iv.Type, &iv.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeInvVect serializes an InvVect to w depending on the protocol version.
|
||||||
|
func writeInvVect(w io.Writer, pver uint32, iv *InvVect) error {
|
||||||
|
return writeElements(w, iv.Type, &iv.Hash)
|
||||||
|
}
|
|
@ -0,0 +1,436 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageHeaderSize is the number of bytes in a bitcoin message header.
|
||||||
|
// Bitcoin network (magic) 4 bytes + command 12 bytes + payload length 4 bytes +
|
||||||
|
// checksum 4 bytes.
|
||||||
|
const MessageHeaderSize = 24
|
||||||
|
|
||||||
|
// CommandSize is the fixed size of all commands in the common bitcoin message
|
||||||
|
// header. Shorter commands must be zero padded.
|
||||||
|
const CommandSize = 12
|
||||||
|
|
||||||
|
// MaxMessagePayload is the maximum bytes a message can be regardless of other
|
||||||
|
// individual limits imposed by messages themselves.
|
||||||
|
const MaxMessagePayload = (1024 * 1024 * 32) // 32MB
|
||||||
|
|
||||||
|
// Commands used in bitcoin message headers which describe the type of message.
|
||||||
|
const (
|
||||||
|
CmdVersion = "version"
|
||||||
|
CmdVerAck = "verack"
|
||||||
|
CmdGetAddr = "getaddr"
|
||||||
|
CmdAddr = "addr"
|
||||||
|
CmdGetBlocks = "getblocks"
|
||||||
|
CmdInv = "inv"
|
||||||
|
CmdGetData = "getdata"
|
||||||
|
CmdNotFound = "notfound"
|
||||||
|
CmdBlock = "block"
|
||||||
|
CmdTx = "tx"
|
||||||
|
CmdGetHeaders = "getheaders"
|
||||||
|
CmdHeaders = "headers"
|
||||||
|
CmdPing = "ping"
|
||||||
|
CmdPong = "pong"
|
||||||
|
CmdAlert = "alert"
|
||||||
|
CmdMemPool = "mempool"
|
||||||
|
CmdFilterAdd = "filteradd"
|
||||||
|
CmdFilterClear = "filterclear"
|
||||||
|
CmdFilterLoad = "filterload"
|
||||||
|
CmdMerkleBlock = "merkleblock"
|
||||||
|
CmdReject = "reject"
|
||||||
|
CmdSendHeaders = "sendheaders"
|
||||||
|
CmdFeeFilter = "feefilter"
|
||||||
|
CmdGetCFilters = "getcfilters"
|
||||||
|
CmdGetCFHeaders = "getcfheaders"
|
||||||
|
CmdGetCFCheckpt = "getcfcheckpt"
|
||||||
|
CmdCFilter = "cfilter"
|
||||||
|
CmdCFHeaders = "cfheaders"
|
||||||
|
CmdCFCheckpt = "cfcheckpt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageEncoding represents the wire message encoding format to be used.
|
||||||
|
type MessageEncoding uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BaseEncoding encodes all messages in the default format specified
|
||||||
|
// for the Bitcoin wire protocol.
|
||||||
|
BaseEncoding MessageEncoding = 1 << iota
|
||||||
|
|
||||||
|
// WitnessEncoding encodes all messages other than transaction messages
|
||||||
|
// using the default Bitcoin wire protocol specification. For transaction
|
||||||
|
// messages, the new encoding format detailed in BIP0144 will be used.
|
||||||
|
WitnessEncoding
|
||||||
|
)
|
||||||
|
|
||||||
|
// LatestEncoding is the most recently specified encoding for the Bitcoin wire
|
||||||
|
// protocol.
|
||||||
|
var LatestEncoding = WitnessEncoding
|
||||||
|
|
||||||
|
// Message is an interface that describes a bitcoin message. A type that
|
||||||
|
// implements Message has complete control over the representation of its data
|
||||||
|
// and may therefore contain additional or fewer fields than those which
|
||||||
|
// are used directly in the protocol encoded message.
|
||||||
|
type Message interface {
|
||||||
|
BtcDecode(io.Reader, uint32, MessageEncoding) error
|
||||||
|
BtcEncode(io.Writer, uint32, MessageEncoding) error
|
||||||
|
Command() string
|
||||||
|
MaxPayloadLength(uint32) uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeEmptyMessage creates a message of the appropriate concrete type based
|
||||||
|
// on the command.
|
||||||
|
func makeEmptyMessage(command string) (Message, error) {
|
||||||
|
var msg Message
|
||||||
|
switch command {
|
||||||
|
case CmdVersion:
|
||||||
|
msg = &MsgVersion{}
|
||||||
|
|
||||||
|
case CmdVerAck:
|
||||||
|
msg = &MsgVerAck{}
|
||||||
|
|
||||||
|
case CmdGetAddr:
|
||||||
|
msg = &MsgGetAddr{}
|
||||||
|
|
||||||
|
case CmdAddr:
|
||||||
|
msg = &MsgAddr{}
|
||||||
|
|
||||||
|
case CmdGetBlocks:
|
||||||
|
msg = &MsgGetBlocks{}
|
||||||
|
|
||||||
|
case CmdBlock:
|
||||||
|
msg = &MsgBlock{}
|
||||||
|
|
||||||
|
case CmdInv:
|
||||||
|
msg = &MsgInv{}
|
||||||
|
|
||||||
|
case CmdGetData:
|
||||||
|
msg = &MsgGetData{}
|
||||||
|
|
||||||
|
case CmdNotFound:
|
||||||
|
msg = &MsgNotFound{}
|
||||||
|
|
||||||
|
case CmdTx:
|
||||||
|
msg = &MsgTx{}
|
||||||
|
|
||||||
|
case CmdPing:
|
||||||
|
msg = &MsgPing{}
|
||||||
|
|
||||||
|
case CmdPong:
|
||||||
|
msg = &MsgPong{}
|
||||||
|
|
||||||
|
case CmdGetHeaders:
|
||||||
|
msg = &MsgGetHeaders{}
|
||||||
|
|
||||||
|
case CmdHeaders:
|
||||||
|
msg = &MsgHeaders{}
|
||||||
|
|
||||||
|
case CmdAlert:
|
||||||
|
msg = &MsgAlert{}
|
||||||
|
|
||||||
|
case CmdMemPool:
|
||||||
|
msg = &MsgMemPool{}
|
||||||
|
|
||||||
|
case CmdFilterAdd:
|
||||||
|
msg = &MsgFilterAdd{}
|
||||||
|
|
||||||
|
case CmdFilterClear:
|
||||||
|
msg = &MsgFilterClear{}
|
||||||
|
|
||||||
|
case CmdFilterLoad:
|
||||||
|
msg = &MsgFilterLoad{}
|
||||||
|
|
||||||
|
case CmdMerkleBlock:
|
||||||
|
msg = &MsgMerkleBlock{}
|
||||||
|
|
||||||
|
case CmdReject:
|
||||||
|
msg = &MsgReject{}
|
||||||
|
|
||||||
|
case CmdSendHeaders:
|
||||||
|
msg = &MsgSendHeaders{}
|
||||||
|
|
||||||
|
case CmdFeeFilter:
|
||||||
|
msg = &MsgFeeFilter{}
|
||||||
|
|
||||||
|
case CmdGetCFilters:
|
||||||
|
msg = &MsgGetCFilters{}
|
||||||
|
|
||||||
|
case CmdGetCFHeaders:
|
||||||
|
msg = &MsgGetCFHeaders{}
|
||||||
|
|
||||||
|
case CmdGetCFCheckpt:
|
||||||
|
msg = &MsgGetCFCheckpt{}
|
||||||
|
|
||||||
|
case CmdCFilter:
|
||||||
|
msg = &MsgCFilter{}
|
||||||
|
|
||||||
|
case CmdCFHeaders:
|
||||||
|
msg = &MsgCFHeaders{}
|
||||||
|
|
||||||
|
case CmdCFCheckpt:
|
||||||
|
msg = &MsgCFCheckpt{}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unhandled command [%s]", command)
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// messageHeader defines the header structure for all bitcoin protocol messages.
|
||||||
|
type messageHeader struct {
|
||||||
|
magic BitcoinNet // 4 bytes
|
||||||
|
command string // 12 bytes
|
||||||
|
length uint32 // 4 bytes
|
||||||
|
checksum [4]byte // 4 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// readMessageHeader reads a bitcoin message header from r.
|
||||||
|
func readMessageHeader(r io.Reader) (int, *messageHeader, error) {
|
||||||
|
// Since readElements doesn't return the amount of bytes read, attempt
|
||||||
|
// to read the entire header into a buffer first in case there is a
|
||||||
|
// short read so the proper amount of read bytes are known. This works
|
||||||
|
// since the header is a fixed size.
|
||||||
|
var headerBytes [MessageHeaderSize]byte
|
||||||
|
n, err := io.ReadFull(r, headerBytes[:])
|
||||||
|
if err != nil {
|
||||||
|
return n, nil, err
|
||||||
|
}
|
||||||
|
hr := bytes.NewReader(headerBytes[:])
|
||||||
|
|
||||||
|
// Create and populate a messageHeader struct from the raw header bytes.
|
||||||
|
hdr := messageHeader{}
|
||||||
|
var command [CommandSize]byte
|
||||||
|
readElements(hr, &hdr.magic, &command, &hdr.length, &hdr.checksum)
|
||||||
|
|
||||||
|
// Strip trailing zeros from command string.
|
||||||
|
hdr.command = string(bytes.TrimRight(command[:], string(0)))
|
||||||
|
|
||||||
|
return n, &hdr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// discardInput reads n bytes from reader r in chunks and discards the read
|
||||||
|
// bytes. This is used to skip payloads when various errors occur and helps
|
||||||
|
// prevent rogue nodes from causing massive memory allocation through forging
|
||||||
|
// header length.
|
||||||
|
func discardInput(r io.Reader, n uint32) {
|
||||||
|
maxSize := uint32(10 * 1024) // 10k at a time
|
||||||
|
numReads := n / maxSize
|
||||||
|
bytesRemaining := n % maxSize
|
||||||
|
if n > 0 {
|
||||||
|
buf := make([]byte, maxSize)
|
||||||
|
for i := uint32(0); i < numReads; i++ {
|
||||||
|
io.ReadFull(r, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if bytesRemaining > 0 {
|
||||||
|
buf := make([]byte, bytesRemaining)
|
||||||
|
io.ReadFull(r, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMessageN writes a bitcoin Message to w including the necessary header
|
||||||
|
// information and returns the number of bytes written. This function is the
|
||||||
|
// same as WriteMessage except it also returns the number of bytes written.
|
||||||
|
func WriteMessageN(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) (int, error) {
|
||||||
|
return WriteMessageWithEncodingN(w, msg, pver, btcnet, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMessage writes a bitcoin Message to w including the necessary header
|
||||||
|
// information. This function is the same as WriteMessageN except it doesn't
|
||||||
|
// doesn't return the number of bytes written. This function is mainly provided
|
||||||
|
// for backwards compatibility with the original API, but it's also useful for
|
||||||
|
// callers that don't care about byte counts.
|
||||||
|
func WriteMessage(w io.Writer, msg Message, pver uint32, btcnet BitcoinNet) error {
|
||||||
|
_, err := WriteMessageN(w, msg, pver, btcnet)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteMessageWithEncodingN writes a bitcoin Message to w including the
|
||||||
|
// necessary header information and returns the number of bytes written.
|
||||||
|
// This function is the same as WriteMessageN except it also allows the caller
|
||||||
|
// to specify the message encoding format to be used when serializing wire
|
||||||
|
// messages.
|
||||||
|
func WriteMessageWithEncodingN(w io.Writer, msg Message, pver uint32,
|
||||||
|
btcnet BitcoinNet, encoding MessageEncoding) (int, error) {
|
||||||
|
|
||||||
|
totalBytes := 0
|
||||||
|
|
||||||
|
// Enforce max command size.
|
||||||
|
var command [CommandSize]byte
|
||||||
|
cmd := msg.Command()
|
||||||
|
if len(cmd) > CommandSize {
|
||||||
|
str := fmt.Sprintf("command [%s] is too long [max %v]",
|
||||||
|
cmd, CommandSize)
|
||||||
|
return totalBytes, messageError("WriteMessage", str)
|
||||||
|
}
|
||||||
|
copy(command[:], []byte(cmd))
|
||||||
|
|
||||||
|
// Encode the message payload.
|
||||||
|
var bw bytes.Buffer
|
||||||
|
err := msg.BtcEncode(&bw, pver, encoding)
|
||||||
|
if err != nil {
|
||||||
|
return totalBytes, err
|
||||||
|
}
|
||||||
|
payload := bw.Bytes()
|
||||||
|
lenp := len(payload)
|
||||||
|
|
||||||
|
// Enforce maximum overall message payload.
|
||||||
|
if lenp > MaxMessagePayload {
|
||||||
|
str := fmt.Sprintf("message payload is too large - encoded "+
|
||||||
|
"%d bytes, but maximum message payload is %d bytes",
|
||||||
|
lenp, MaxMessagePayload)
|
||||||
|
return totalBytes, messageError("WriteMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce maximum message payload based on the message type.
|
||||||
|
mpl := msg.MaxPayloadLength(pver)
|
||||||
|
if uint32(lenp) > mpl {
|
||||||
|
str := fmt.Sprintf("message payload is too large - encoded "+
|
||||||
|
"%d bytes, but maximum message payload size for "+
|
||||||
|
"messages of type [%s] is %d.", lenp, cmd, mpl)
|
||||||
|
return totalBytes, messageError("WriteMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create header for the message.
|
||||||
|
hdr := messageHeader{}
|
||||||
|
hdr.magic = btcnet
|
||||||
|
hdr.command = cmd
|
||||||
|
hdr.length = uint32(lenp)
|
||||||
|
copy(hdr.checksum[:], chainhash.DoubleHashB(payload)[0:4])
|
||||||
|
|
||||||
|
// Encode the header for the message. This is done to a buffer
|
||||||
|
// rather than directly to the writer since writeElements doesn't
|
||||||
|
// return the number of bytes written.
|
||||||
|
hw := bytes.NewBuffer(make([]byte, 0, MessageHeaderSize))
|
||||||
|
writeElements(hw, hdr.magic, command, hdr.length, hdr.checksum)
|
||||||
|
|
||||||
|
// Write header.
|
||||||
|
n, err := w.Write(hw.Bytes())
|
||||||
|
totalBytes += n
|
||||||
|
if err != nil {
|
||||||
|
return totalBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write payload.
|
||||||
|
n, err = w.Write(payload)
|
||||||
|
totalBytes += n
|
||||||
|
return totalBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMessageWithEncodingN reads, validates, and parses the next bitcoin Message
|
||||||
|
// from r for the provided protocol version and bitcoin network. It returns the
|
||||||
|
// number of bytes read in addition to the parsed Message and raw bytes which
|
||||||
|
// comprise the message. This function is the same as ReadMessageN except it
|
||||||
|
// allows the caller to specify which message encoding is to to consult when
|
||||||
|
// decoding wire messages.
|
||||||
|
func ReadMessageWithEncodingN(r io.Reader, pver uint32, btcnet BitcoinNet,
|
||||||
|
enc MessageEncoding) (int, Message, []byte, error) {
|
||||||
|
|
||||||
|
totalBytes := 0
|
||||||
|
n, hdr, err := readMessageHeader(r)
|
||||||
|
totalBytes += n
|
||||||
|
if err != nil {
|
||||||
|
return totalBytes, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce maximum message payload.
|
||||||
|
if hdr.length > MaxMessagePayload {
|
||||||
|
str := fmt.Sprintf("message payload is too large - header "+
|
||||||
|
"indicates %d bytes, but max message payload is %d "+
|
||||||
|
"bytes.", hdr.length, MaxMessagePayload)
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage", str)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for messages from the wrong bitcoin network.
|
||||||
|
if hdr.magic != btcnet {
|
||||||
|
discardInput(r, hdr.length)
|
||||||
|
str := fmt.Sprintf("message from other network [%v]", hdr.magic)
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for malformed commands.
|
||||||
|
command := hdr.command
|
||||||
|
if !utf8.ValidString(command) {
|
||||||
|
discardInput(r, hdr.length)
|
||||||
|
str := fmt.Sprintf("invalid command %v", []byte(command))
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create struct of appropriate message type based on the command.
|
||||||
|
msg, err := makeEmptyMessage(command)
|
||||||
|
if err != nil {
|
||||||
|
discardInput(r, hdr.length)
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage",
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for maximum length based on the message type as a malicious client
|
||||||
|
// could otherwise create a well-formed header and set the length to max
|
||||||
|
// numbers in order to exhaust the machine's memory.
|
||||||
|
mpl := msg.MaxPayloadLength(pver)
|
||||||
|
if hdr.length > mpl {
|
||||||
|
discardInput(r, hdr.length)
|
||||||
|
str := fmt.Sprintf("payload exceeds max length - header "+
|
||||||
|
"indicates %v bytes, but max payload size for "+
|
||||||
|
"messages of type [%v] is %v.", hdr.length, command, mpl)
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read payload.
|
||||||
|
payload := make([]byte, hdr.length)
|
||||||
|
n, err = io.ReadFull(r, payload)
|
||||||
|
totalBytes += n
|
||||||
|
if err != nil {
|
||||||
|
return totalBytes, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test checksum.
|
||||||
|
checksum := chainhash.DoubleHashB(payload)[0:4]
|
||||||
|
if !bytes.Equal(checksum[:], hdr.checksum[:]) {
|
||||||
|
str := fmt.Sprintf("payload checksum failed - header "+
|
||||||
|
"indicates %v, but actual checksum is %v.",
|
||||||
|
hdr.checksum, checksum)
|
||||||
|
return totalBytes, nil, nil, messageError("ReadMessage", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal message. NOTE: This must be a *bytes.Buffer since the
|
||||||
|
// MsgVersion BtcDecode function requires it.
|
||||||
|
pr := bytes.NewBuffer(payload)
|
||||||
|
err = msg.BtcDecode(pr, pver, enc)
|
||||||
|
if err != nil {
|
||||||
|
return totalBytes, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalBytes, msg, payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMessageN reads, validates, and parses the next bitcoin Message from r for
|
||||||
|
// the provided protocol version and bitcoin network. It returns the number of
|
||||||
|
// bytes read in addition to the parsed Message and raw bytes which comprise the
|
||||||
|
// message. This function is the same as ReadMessage except it also returns the
|
||||||
|
// number of bytes read.
|
||||||
|
func ReadMessageN(r io.Reader, pver uint32, btcnet BitcoinNet) (int, Message, []byte, error) {
|
||||||
|
return ReadMessageWithEncodingN(r, pver, btcnet, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMessage reads, validates, and parses the next bitcoin Message from r for
|
||||||
|
// the provided protocol version and bitcoin network. It returns the parsed
|
||||||
|
// Message and raw bytes which comprise the message. This function only differs
|
||||||
|
// from ReadMessageN in that it doesn't return the number of bytes read. This
|
||||||
|
// function is mainly provided for backwards compatibility with the original
|
||||||
|
// API, but it's also useful for callers that don't care about byte counts.
|
||||||
|
func ReadMessage(r io.Reader, pver uint32, btcnet BitcoinNet) (Message, []byte, error) {
|
||||||
|
_, msg, buf, err := ReadMessageN(r, pver, btcnet)
|
||||||
|
return msg, buf, err
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxAddrPerMsg is the maximum number of addresses that can be in a single
|
||||||
|
// bitcoin addr message (MsgAddr).
|
||||||
|
const MaxAddrPerMsg = 1000
|
||||||
|
|
||||||
|
// MsgAddr implements the Message interface and represents a bitcoin
|
||||||
|
// addr message. It is used to provide a list of known active peers on the
|
||||||
|
// network. An active peer is considered one that has transmitted a message
|
||||||
|
// within the last 3 hours. Nodes which have not transmitted in that time
|
||||||
|
// frame should be forgotten. Each message is limited to a maximum number of
|
||||||
|
// addresses, which is currently 1000. As a result, multiple messages must
|
||||||
|
// be used to relay the full list.
|
||||||
|
//
|
||||||
|
// Use the AddAddress function to build up the list of known addresses when
|
||||||
|
// sending an addr message to another peer.
|
||||||
|
type MsgAddr struct {
|
||||||
|
AddrList []*NetAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAddress adds a known active peer to the message.
|
||||||
|
func (msg *MsgAddr) AddAddress(na *NetAddress) error {
|
||||||
|
if len(msg.AddrList)+1 > MaxAddrPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses in message [max %v]",
|
||||||
|
MaxAddrPerMsg)
|
||||||
|
return messageError("MsgAddr.AddAddress", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.AddrList = append(msg.AddrList, na)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAddresses adds multiple known active peers to the message.
|
||||||
|
func (msg *MsgAddr) AddAddresses(netAddrs ...*NetAddress) error {
|
||||||
|
for _, na := range netAddrs {
|
||||||
|
err := msg.AddAddress(na)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAddresses removes all addresses from the message.
|
||||||
|
func (msg *MsgAddr) ClearAddresses() {
|
||||||
|
msg.AddrList = []*NetAddress{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAddr) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max addresses per message.
|
||||||
|
if count > MaxAddrPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses for message "+
|
||||||
|
"[count %v, max %v]", count, MaxAddrPerMsg)
|
||||||
|
return messageError("MsgAddr.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrList := make([]NetAddress, count)
|
||||||
|
msg.AddrList = make([]*NetAddress, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
na := &addrList[i]
|
||||||
|
err := readNetAddress(r, pver, na, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddAddress(na)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAddr) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Protocol versions before MultipleAddressVersion only allowed 1 address
|
||||||
|
// per message.
|
||||||
|
count := len(msg.AddrList)
|
||||||
|
if pver < MultipleAddressVersion && count > 1 {
|
||||||
|
str := fmt.Sprintf("too many addresses for message of "+
|
||||||
|
"protocol version %v [count %v, max 1]", pver, count)
|
||||||
|
return messageError("MsgAddr.BtcEncode", str)
|
||||||
|
|
||||||
|
}
|
||||||
|
if count > MaxAddrPerMsg {
|
||||||
|
str := fmt.Sprintf("too many addresses for message "+
|
||||||
|
"[count %v, max %v]", count, MaxAddrPerMsg)
|
||||||
|
return messageError("MsgAddr.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, na := range msg.AddrList {
|
||||||
|
err = writeNetAddress(w, pver, na, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgAddr) Command() string {
|
||||||
|
return CmdAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAddr) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
if pver < MultipleAddressVersion {
|
||||||
|
// Num addresses (varInt) + a single net addresses.
|
||||||
|
return MaxVarIntPayload + maxNetAddressPayload(pver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num addresses (varInt) + max allowed addresses.
|
||||||
|
return MaxVarIntPayload + (MaxAddrPerMsg * maxNetAddressPayload(pver))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgAddr returns a new bitcoin addr message that conforms to the
|
||||||
|
// Message interface. See MsgAddr for details.
|
||||||
|
func NewMsgAddr() *MsgAddr {
|
||||||
|
return &MsgAddr{
|
||||||
|
AddrList: make([]*NetAddress, 0, MaxAddrPerMsg),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,407 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgAlert contains a payload and a signature:
|
||||||
|
//
|
||||||
|
// ===============================================
|
||||||
|
// | Field | Data Type | Size |
|
||||||
|
// ===============================================
|
||||||
|
// | payload | []uchar | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | signature | []uchar | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
//
|
||||||
|
// Here payload is an Alert serialized into a byte array to ensure that
|
||||||
|
// versions using incompatible alert formats can still relay
|
||||||
|
// alerts among one another.
|
||||||
|
//
|
||||||
|
// An Alert is the payload deserialized as follows:
|
||||||
|
//
|
||||||
|
// ===============================================
|
||||||
|
// | Field | Data Type | Size |
|
||||||
|
// ===============================================
|
||||||
|
// | Version | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | RelayUntil | int64 | 8 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Expiration | int64 | 8 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | ID | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Cancel | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | SetCancel | set<int32> | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | MinVer | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | MaxVer | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | SetSubVer | set<string> | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Priority | int32 | 4 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Comment | string | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | StatusBar | string | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Reserved | string | ? |
|
||||||
|
// -----------------------------------------------
|
||||||
|
// | Total (Fixed) | 45 |
|
||||||
|
// -----------------------------------------------
|
||||||
|
//
|
||||||
|
// NOTE:
|
||||||
|
// * string is a VarString i.e VarInt length followed by the string itself
|
||||||
|
// * set<string> is a VarInt followed by as many number of strings
|
||||||
|
// * set<int32> is a VarInt followed by as many number of ints
|
||||||
|
// * fixedAlertSize = 40 + 5*min(VarInt) = 40 + 5*1 = 45
|
||||||
|
//
|
||||||
|
// Now we can define bounds on Alert size, SetCancel and SetSubVer
|
||||||
|
|
||||||
|
// Fixed size of the alert payload
|
||||||
|
const fixedAlertSize = 45
|
||||||
|
|
||||||
|
// maxSignatureSize is the max size of an ECDSA signature.
|
||||||
|
// NOTE: Since this size is fixed and < 255, the size of VarInt required = 1.
|
||||||
|
const maxSignatureSize = 72
|
||||||
|
|
||||||
|
// maxAlertSize is the maximum size an alert.
|
||||||
|
//
|
||||||
|
// MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature
|
||||||
|
// MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1
|
||||||
|
const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1
|
||||||
|
|
||||||
|
// maxCountSetCancel is the maximum number of cancel IDs that could possibly
|
||||||
|
// fit into a maximum size alert.
|
||||||
|
//
|
||||||
|
// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
|
||||||
|
// for caculating maximum number of cancel IDs, set all other var sizes to 0
|
||||||
|
// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(int32)
|
||||||
|
// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
|
||||||
|
const maxCountSetCancel = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
|
||||||
|
|
||||||
|
// maxCountSetSubVer is the maximum number of subversions that could possibly
|
||||||
|
// fit into a maximum size alert.
|
||||||
|
//
|
||||||
|
// maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
|
||||||
|
// for caculating maximum number of subversions, set all other var sizes to 0
|
||||||
|
// maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(string)
|
||||||
|
// x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / sizeOf(string)
|
||||||
|
// subversion would typically be something like "/Satoshi:0.7.2/" (15 bytes)
|
||||||
|
// so assuming < 255 bytes, sizeOf(string) = sizeOf(uint8) + 255 = 256
|
||||||
|
const maxCountSetSubVer = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 256
|
||||||
|
|
||||||
|
// Alert contains the data deserialized from the MsgAlert payload.
|
||||||
|
type Alert struct {
|
||||||
|
// Alert format version
|
||||||
|
Version int32
|
||||||
|
|
||||||
|
// Timestamp beyond which nodes should stop relaying this alert
|
||||||
|
RelayUntil int64
|
||||||
|
|
||||||
|
// Timestamp beyond which this alert is no longer in effect and
|
||||||
|
// should be ignored
|
||||||
|
Expiration int64
|
||||||
|
|
||||||
|
// A unique ID number for this alert
|
||||||
|
ID int32
|
||||||
|
|
||||||
|
// All alerts with an ID less than or equal to this number should
|
||||||
|
// cancelled, deleted and not accepted in the future
|
||||||
|
Cancel int32
|
||||||
|
|
||||||
|
// All alert IDs contained in this set should be cancelled as above
|
||||||
|
SetCancel []int32
|
||||||
|
|
||||||
|
// This alert only applies to versions greater than or equal to this
|
||||||
|
// version. Other versions should still relay it.
|
||||||
|
MinVer int32
|
||||||
|
|
||||||
|
// This alert only applies to versions less than or equal to this version.
|
||||||
|
// Other versions should still relay it.
|
||||||
|
MaxVer int32
|
||||||
|
|
||||||
|
// If this set contains any elements, then only nodes that have their
|
||||||
|
// subVer contained in this set are affected by the alert. Other versions
|
||||||
|
// should still relay it.
|
||||||
|
SetSubVer []string
|
||||||
|
|
||||||
|
// Relative priority compared to other alerts
|
||||||
|
Priority int32
|
||||||
|
|
||||||
|
// A comment on the alert that is not displayed
|
||||||
|
Comment string
|
||||||
|
|
||||||
|
// The alert message that is displayed to the user
|
||||||
|
StatusBar string
|
||||||
|
|
||||||
|
// Reserved
|
||||||
|
Reserved string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes the alert to w using the alert protocol encoding format.
|
||||||
|
func (alert *Alert) Serialize(w io.Writer, pver uint32) error {
|
||||||
|
err := writeElements(w, alert.Version, alert.RelayUntil,
|
||||||
|
alert.Expiration, alert.ID, alert.Cancel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count := len(alert.SetCancel)
|
||||||
|
if count > maxCountSetCancel {
|
||||||
|
str := fmt.Sprintf("too many cancel alert IDs for alert "+
|
||||||
|
"[count %v, max %v]", count, maxCountSetCancel)
|
||||||
|
return messageError("Alert.Serialize", str)
|
||||||
|
}
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
err = writeElement(w, alert.SetCancel[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElements(w, alert.MinVer, alert.MaxVer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
count = len(alert.SetSubVer)
|
||||||
|
if count > maxCountSetSubVer {
|
||||||
|
str := fmt.Sprintf("too many sub versions for alert "+
|
||||||
|
"[count %v, max %v]", count, maxCountSetSubVer)
|
||||||
|
return messageError("Alert.Serialize", str)
|
||||||
|
}
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
err = WriteVarString(w, pver, alert.SetSubVer[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, alert.Priority)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = WriteVarString(w, pver, alert.Comment)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = WriteVarString(w, pver, alert.StatusBar)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return WriteVarString(w, pver, alert.Reserved)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes from r into the receiver using the alert protocol
|
||||||
|
// encoding format.
|
||||||
|
func (alert *Alert) Deserialize(r io.Reader, pver uint32) error {
|
||||||
|
err := readElements(r, &alert.Version, &alert.RelayUntil,
|
||||||
|
&alert.Expiration, &alert.ID, &alert.Cancel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCancel: first read a VarInt that contains
|
||||||
|
// count - the number of Cancel IDs, then
|
||||||
|
// iterate count times and read them
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > maxCountSetCancel {
|
||||||
|
str := fmt.Sprintf("too many cancel alert IDs for alert "+
|
||||||
|
"[count %v, max %v]", count, maxCountSetCancel)
|
||||||
|
return messageError("Alert.Deserialize", str)
|
||||||
|
}
|
||||||
|
alert.SetCancel = make([]int32, count)
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
err := readElement(r, &alert.SetCancel[i])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElements(r, &alert.MinVer, &alert.MaxVer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSubVer: similar to SetCancel
|
||||||
|
// but read count number of sub-version strings
|
||||||
|
count, err = ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > maxCountSetSubVer {
|
||||||
|
str := fmt.Sprintf("too many sub versions for alert "+
|
||||||
|
"[count %v, max %v]", count, maxCountSetSubVer)
|
||||||
|
return messageError("Alert.Deserialize", str)
|
||||||
|
}
|
||||||
|
alert.SetSubVer = make([]string, count)
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
alert.SetSubVer[i], err = ReadVarString(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElement(r, &alert.Priority)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
alert.Comment, err = ReadVarString(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
alert.StatusBar, err = ReadVarString(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
alert.Reserved, err = ReadVarString(r, pver)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAlert returns an new Alert with values provided.
|
||||||
|
func NewAlert(version int32, relayUntil int64, expiration int64,
|
||||||
|
id int32, cancel int32, setCancel []int32, minVer int32,
|
||||||
|
maxVer int32, setSubVer []string, priority int32, comment string,
|
||||||
|
statusBar string) *Alert {
|
||||||
|
return &Alert{
|
||||||
|
Version: version,
|
||||||
|
RelayUntil: relayUntil,
|
||||||
|
Expiration: expiration,
|
||||||
|
ID: id,
|
||||||
|
Cancel: cancel,
|
||||||
|
SetCancel: setCancel,
|
||||||
|
MinVer: minVer,
|
||||||
|
MaxVer: maxVer,
|
||||||
|
SetSubVer: setSubVer,
|
||||||
|
Priority: priority,
|
||||||
|
Comment: comment,
|
||||||
|
StatusBar: statusBar,
|
||||||
|
Reserved: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAlertFromPayload returns an Alert with values deserialized from the
|
||||||
|
// serialized payload.
|
||||||
|
func NewAlertFromPayload(serializedPayload []byte, pver uint32) (*Alert, error) {
|
||||||
|
var alert Alert
|
||||||
|
r := bytes.NewReader(serializedPayload)
|
||||||
|
err := alert.Deserialize(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &alert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgAlert implements the Message interface and defines a bitcoin alert
|
||||||
|
// message.
|
||||||
|
//
|
||||||
|
// This is a signed message that provides notifications that the client should
|
||||||
|
// display if the signature matches the key. bitcoind/bitcoin-qt only checks
|
||||||
|
// against a signature from the core developers.
|
||||||
|
type MsgAlert struct {
|
||||||
|
// SerializedPayload is the alert payload serialized as a string so that the
|
||||||
|
// version can change but the Alert can still be passed on by older
|
||||||
|
// clients.
|
||||||
|
SerializedPayload []byte
|
||||||
|
|
||||||
|
// Signature is the ECDSA signature of the message.
|
||||||
|
Signature []byte
|
||||||
|
|
||||||
|
// Deserialized Payload
|
||||||
|
Payload *Alert
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAlert) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
msg.SerializedPayload, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||||
|
"alert serialized payload")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Payload, err = NewAlertFromPayload(msg.SerializedPayload, pver)
|
||||||
|
if err != nil {
|
||||||
|
msg.Payload = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Signature, err = ReadVarBytes(r, pver, MaxMessagePayload,
|
||||||
|
"alert signature")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAlert) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
var err error
|
||||||
|
var serializedpayload []byte
|
||||||
|
if msg.Payload != nil {
|
||||||
|
// try to Serialize Payload if possible
|
||||||
|
r := new(bytes.Buffer)
|
||||||
|
err = msg.Payload.Serialize(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
// Serialize failed - ignore & fallback
|
||||||
|
// to SerializedPayload
|
||||||
|
serializedpayload = msg.SerializedPayload
|
||||||
|
} else {
|
||||||
|
serializedpayload = r.Bytes()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
serializedpayload = msg.SerializedPayload
|
||||||
|
}
|
||||||
|
slen := uint64(len(serializedpayload))
|
||||||
|
if slen == 0 {
|
||||||
|
return messageError("MsgAlert.BtcEncode", "empty serialized payload")
|
||||||
|
}
|
||||||
|
err = WriteVarBytes(w, pver, serializedpayload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return WriteVarBytes(w, pver, msg.Signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgAlert) Command() string {
|
||||||
|
return CmdAlert
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgAlert) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Since this can vary depending on the message, make it the max
|
||||||
|
// size allowed.
|
||||||
|
return MaxMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgAlert returns a new bitcoin alert message that conforms to the Message
|
||||||
|
// interface. See MsgAlert for details.
|
||||||
|
func NewMsgAlert(serializedPayload []byte, signature []byte) *MsgAlert {
|
||||||
|
return &MsgAlert{
|
||||||
|
SerializedPayload: serializedPayload,
|
||||||
|
Signature: signature,
|
||||||
|
Payload: nil,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,290 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultTransactionAlloc is the default size used for the backing array
|
||||||
|
// for transactions. The transaction array will dynamically grow as needed, but
|
||||||
|
// this figure is intended to provide enough space for the number of
|
||||||
|
// transactions in the vast majority of blocks without needing to grow the
|
||||||
|
// backing array multiple times.
|
||||||
|
const defaultTransactionAlloc = 2048
|
||||||
|
|
||||||
|
// MaxBlocksPerMsg is the maximum number of blocks allowed per message.
|
||||||
|
const MaxBlocksPerMsg = 500
|
||||||
|
|
||||||
|
// MaxBlockPayload is the maximum bytes a block message can be in bytes.
|
||||||
|
// After Segregated Witness, the max block payload has been raised to 4MB.
|
||||||
|
const MaxBlockPayload = 4000000
|
||||||
|
|
||||||
|
// maxTxPerBlock is the maximum number of transactions that could
|
||||||
|
// possibly fit into a block.
|
||||||
|
const maxTxPerBlock = (MaxBlockPayload / minTxPayload) + 1
|
||||||
|
|
||||||
|
// TxLoc holds locator data for the offset and length of where a transaction is
|
||||||
|
// located within a MsgBlock data buffer.
|
||||||
|
type TxLoc struct {
|
||||||
|
TxStart int
|
||||||
|
TxLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgBlock implements the Message interface and represents a bitcoin
|
||||||
|
// block message. It is used to deliver block and transaction information in
|
||||||
|
// response to a getdata message (MsgGetData) for a given block hash.
|
||||||
|
type MsgBlock struct {
|
||||||
|
Header BlockHeader
|
||||||
|
Transactions []*MsgTx
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransaction adds a transaction to the message.
|
||||||
|
func (msg *MsgBlock) AddTransaction(tx *MsgTx) error {
|
||||||
|
msg.Transactions = append(msg.Transactions, tx)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearTransactions removes all transactions from the message.
|
||||||
|
func (msg *MsgBlock) ClearTransactions() {
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, defaultTransactionAlloc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Deserialize for decoding blocks stored to disk, such as in a database, as
|
||||||
|
// opposed to decoding blocks from the wire.
|
||||||
|
func (msg *MsgBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
err := readBlockHeader(r, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txCount, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent more transactions than could possibly fit into a block.
|
||||||
|
// It would be possible to cause memory exhaustion and panics without
|
||||||
|
// a sane upper bound on this count.
|
||||||
|
if txCount > maxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transactions to fit into a block "+
|
||||||
|
"[count %d, max %d]", txCount, maxTxPerBlock)
|
||||||
|
return messageError("MsgBlock.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, txCount)
|
||||||
|
for i := uint64(0); i < txCount; i++ {
|
||||||
|
tx := MsgTx{}
|
||||||
|
err := tx.BtcDecode(r, pver, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Transactions = append(msg.Transactions, &tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a block from r into the receiver using a format that is
|
||||||
|
// suitable for long-term storage such as a database while respecting the
|
||||||
|
// Version field in the block. This function differs from BtcDecode in that
|
||||||
|
// BtcDecode decodes from the bitcoin wire protocol as it was sent across the
|
||||||
|
// network. The wire encoding can technically differ depending on the protocol
|
||||||
|
// version and doesn't even really need to match the format of a stored block at
|
||||||
|
// all. As of the time this comment was written, the encoded block is the same
|
||||||
|
// in both instances, but there is a distinct difference and separating the two
|
||||||
|
// allows the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgBlock) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of BtcDecode.
|
||||||
|
//
|
||||||
|
// Passing an encoding type of WitnessEncoding to BtcEncode for the
|
||||||
|
// MessageEncoding parameter indicates that the transactions within the
|
||||||
|
// block are expected to be serialized according to the new
|
||||||
|
// serialization structure defined in BIP0141.
|
||||||
|
return msg.BtcDecode(r, 0, WitnessEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeserializeNoWitness decodes a block from r into the receiver similar to
|
||||||
|
// Deserialize, however DeserializeWitness strips all (if any) witness data
|
||||||
|
// from the transactions within the block before encoding them.
|
||||||
|
func (msg *MsgBlock) DeserializeNoWitness(r io.Reader) error {
|
||||||
|
return msg.BtcDecode(r, 0, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeserializeTxLoc decodes r in the same manner Deserialize does, but it takes
|
||||||
|
// a byte buffer instead of a generic reader and returns a slice containing the
|
||||||
|
// start and length of each transaction within the raw data that is being
|
||||||
|
// deserialized.
|
||||||
|
func (msg *MsgBlock) DeserializeTxLoc(r *bytes.Buffer) ([]TxLoc, error) {
|
||||||
|
fullLen := r.Len()
|
||||||
|
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of existing wire protocol functions.
|
||||||
|
err := readBlockHeader(r, 0, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
txCount, err := ReadVarInt(r, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent more transactions than could possibly fit into a block.
|
||||||
|
// It would be possible to cause memory exhaustion and panics without
|
||||||
|
// a sane upper bound on this count.
|
||||||
|
if txCount > maxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transactions to fit into a block "+
|
||||||
|
"[count %d, max %d]", txCount, maxTxPerBlock)
|
||||||
|
return nil, messageError("MsgBlock.DeserializeTxLoc", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize each transaction while keeping track of its location
|
||||||
|
// within the byte stream.
|
||||||
|
msg.Transactions = make([]*MsgTx, 0, txCount)
|
||||||
|
txLocs := make([]TxLoc, txCount)
|
||||||
|
for i := uint64(0); i < txCount; i++ {
|
||||||
|
txLocs[i].TxStart = fullLen - r.Len()
|
||||||
|
tx := MsgTx{}
|
||||||
|
err := tx.Deserialize(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msg.Transactions = append(msg.Transactions, &tx)
|
||||||
|
txLocs[i].TxLen = (fullLen - r.Len()) - txLocs[i].TxStart
|
||||||
|
}
|
||||||
|
|
||||||
|
return txLocs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
// See Serialize for encoding blocks to be stored to disk, such as in a
|
||||||
|
// database, as opposed to encoding blocks for the wire.
|
||||||
|
func (msg *MsgBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
err := writeBlockHeader(w, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, pver, uint64(len(msg.Transactions)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
err = tx.BtcEncode(w, pver, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize encodes the block to w using a format that suitable for long-term
|
||||||
|
// storage such as a database while respecting the Version field in the block.
|
||||||
|
// This function differs from BtcEncode in that BtcEncode encodes the block to
|
||||||
|
// the bitcoin wire protocol in order to be sent across the network. The wire
|
||||||
|
// encoding can technically differ depending on the protocol version and doesn't
|
||||||
|
// even really need to match the format of a stored block at all. As of the
|
||||||
|
// time this comment was written, the encoded block is the same in both
|
||||||
|
// instances, but there is a distinct difference and separating the two allows
|
||||||
|
// the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgBlock) Serialize(w io.Writer) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// at protocol version 0 and the stable long-term storage format. As
|
||||||
|
// a result, make use of BtcEncode.
|
||||||
|
//
|
||||||
|
// Passing WitnessEncoding as the encoding type here indicates that
|
||||||
|
// each of the transactions should be serialized using the witness
|
||||||
|
// serialization structure defined in BIP0141.
|
||||||
|
return msg.BtcEncode(w, 0, WitnessEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeNoWitness encodes a block to w using an identical format to
|
||||||
|
// Serialize, with all (if any) witness data stripped from all transactions.
|
||||||
|
// This method is provided in additon to the regular Serialize, in order to
|
||||||
|
// allow one to selectively encode transaction witness data to non-upgraded
|
||||||
|
// peers which are unaware of the new encoding.
|
||||||
|
func (msg *MsgBlock) SerializeNoWitness(w io.Writer) error {
|
||||||
|
return msg.BtcEncode(w, 0, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeSize returns the number of bytes it would take to serialize the
|
||||||
|
// block, factoring in any witness data within transaction.
|
||||||
|
func (msg *MsgBlock) SerializeSize() int {
|
||||||
|
// Block header bytes + Serialized varint size for the number of
|
||||||
|
// transactions.
|
||||||
|
n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions)))
|
||||||
|
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
n += tx.SerializeSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeSizeStripped returns the number of bytes it would take to serialize
|
||||||
|
// the block, excluding any witness data (if any).
|
||||||
|
func (msg *MsgBlock) SerializeSizeStripped() int {
|
||||||
|
// Block header bytes + Serialized varint size for the number of
|
||||||
|
// transactions.
|
||||||
|
n := blockHeaderLen + VarIntSerializeSize(uint64(len(msg.Transactions)))
|
||||||
|
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
n += tx.SerializeSizeStripped()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgBlock) Command() string {
|
||||||
|
return CmdBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Block header at 80 bytes + transaction count + max transactions
|
||||||
|
// which can vary up to the MaxBlockPayload (including the block header
|
||||||
|
// and transaction count).
|
||||||
|
return MaxBlockPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockHash computes the block identifier hash for this block.
|
||||||
|
func (msg *MsgBlock) BlockHash() chainhash.Hash {
|
||||||
|
return msg.Header.BlockHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxHashes returns a slice of hashes of all of transactions in this block.
|
||||||
|
func (msg *MsgBlock) TxHashes() ([]chainhash.Hash, error) {
|
||||||
|
hashList := make([]chainhash.Hash, 0, len(msg.Transactions))
|
||||||
|
for _, tx := range msg.Transactions {
|
||||||
|
hashList = append(hashList, tx.TxHash())
|
||||||
|
}
|
||||||
|
return hashList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgBlock returns a new bitcoin block message that conforms to the
|
||||||
|
// Message interface. See MsgBlock for details.
|
||||||
|
func NewMsgBlock(blockHeader *BlockHeader) *MsgBlock {
|
||||||
|
return &MsgBlock{
|
||||||
|
Header: *blockHeader,
|
||||||
|
Transactions: make([]*MsgTx, 0, defaultTransactionAlloc),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
// Copyright (c) 2018 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CFCheckptInterval is the gap (in number of blocks) between each
|
||||||
|
// filter header checkpoint.
|
||||||
|
CFCheckptInterval = 1000
|
||||||
|
|
||||||
|
// maxCFHeadersLen is the max number of filter headers we will attempt
|
||||||
|
// to decode.
|
||||||
|
maxCFHeadersLen = 100000
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrInsaneCFHeaderCount signals that we were asked to decode an
|
||||||
|
// unreasonable number of cfilter headers.
|
||||||
|
var ErrInsaneCFHeaderCount = errors.New(
|
||||||
|
"refusing to decode unreasonable number of filter headers")
|
||||||
|
|
||||||
|
// MsgCFCheckpt implements the Message interface and represents a bitcoin
|
||||||
|
// cfcheckpt message. It is used to deliver committed filter header information
|
||||||
|
// in response to a getcfcheckpt message (MsgGetCFCheckpt). See MsgGetCFCheckpt
|
||||||
|
// for details on requesting the headers.
|
||||||
|
type MsgCFCheckpt struct {
|
||||||
|
FilterType FilterType
|
||||||
|
StopHash chainhash.Hash
|
||||||
|
FilterHeaders []*chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCFHeader adds a new committed filter header to the message.
|
||||||
|
func (msg *MsgCFCheckpt) AddCFHeader(header *chainhash.Hash) error {
|
||||||
|
if len(msg.FilterHeaders) == cap(msg.FilterHeaders) {
|
||||||
|
str := fmt.Sprintf("FilterHeaders has insufficient capacity for "+
|
||||||
|
"additional header: len = %d", len(msg.FilterHeaders))
|
||||||
|
return messageError("MsgCFCheckpt.AddCFHeader", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.FilterHeaders = append(msg.FilterHeaders, header)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
// Read filter type
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read stop hash
|
||||||
|
err = readElement(r, &msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read number of filter headers
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refuse to decode an insane number of cfheaders.
|
||||||
|
if count > maxCFHeadersLen {
|
||||||
|
return ErrInsaneCFHeaderCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
msg.FilterHeaders = make([]*chainhash.Hash, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
var cfh chainhash.Hash
|
||||||
|
err := readElement(r, &cfh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.FilterHeaders[i] = &cfh
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
// Write filter type
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write stop hash
|
||||||
|
err = writeElement(w, msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write length of FilterHeaders slice
|
||||||
|
count := len(msg.FilterHeaders)
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cfh := range msg.FilterHeaders {
|
||||||
|
err := writeElement(w, cfh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a filter header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database. This function
|
||||||
|
// differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
|
||||||
|
// protocol as it was sent across the network. The wire encoding can
|
||||||
|
// technically differ depending on the protocol version and doesn't even really
|
||||||
|
// need to match the format of a stored filter header at all. As of the time
|
||||||
|
// this comment was written, the encoded filter header is the same in both
|
||||||
|
// instances, but there is a distinct difference and separating the two allows
|
||||||
|
// the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgCFCheckpt) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// and the stable long-term storage format. As a result, make use of
|
||||||
|
// BtcDecode.
|
||||||
|
return msg.BtcDecode(r, 0, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgCFCheckpt) Command() string {
|
||||||
|
return CmdCFCheckpt
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFCheckpt) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Message size depends on the blockchain height, so return general limit
|
||||||
|
// for all messages.
|
||||||
|
return MaxMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgCFCheckpt returns a new bitcoin cfheaders message that conforms to
|
||||||
|
// the Message interface. See MsgCFCheckpt for details.
|
||||||
|
func NewMsgCFCheckpt(filterType FilterType, stopHash *chainhash.Hash,
|
||||||
|
headersCount int) *MsgCFCheckpt {
|
||||||
|
return &MsgCFCheckpt{
|
||||||
|
FilterType: filterType,
|
||||||
|
StopHash: *stopHash,
|
||||||
|
FilterHeaders: make([]*chainhash.Hash, 0, headersCount),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxCFHeaderPayload is the maximum byte size of a committed
|
||||||
|
// filter header.
|
||||||
|
MaxCFHeaderPayload = chainhash.HashSize
|
||||||
|
|
||||||
|
// MaxCFHeadersPerMsg is the maximum number of committed filter headers
|
||||||
|
// that can be in a single bitcoin cfheaders message.
|
||||||
|
MaxCFHeadersPerMsg = 2000
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgCFHeaders implements the Message interface and represents a bitcoin
|
||||||
|
// cfheaders message. It is used to deliver committed filter header information
|
||||||
|
// in response to a getcfheaders message (MsgGetCFHeaders). The maximum number
|
||||||
|
// of committed filter headers per message is currently 2000. See
|
||||||
|
// MsgGetCFHeaders for details on requesting the headers.
|
||||||
|
type MsgCFHeaders struct {
|
||||||
|
FilterType FilterType
|
||||||
|
StopHash chainhash.Hash
|
||||||
|
PrevFilterHeader chainhash.Hash
|
||||||
|
FilterHashes []*chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCFHash adds a new filter hash to the message.
|
||||||
|
func (msg *MsgCFHeaders) AddCFHash(hash *chainhash.Hash) error {
|
||||||
|
if len(msg.FilterHashes)+1 > MaxCFHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block headers in message [max %v]",
|
||||||
|
MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgCFHeaders.AddCFHash", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.FilterHashes = append(msg.FilterHashes, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
// Read filter type
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read stop hash
|
||||||
|
err = readElement(r, &msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read prev filter header
|
||||||
|
err = readElement(r, &msg.PrevFilterHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read number of filter headers
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max committed filter headers per message.
|
||||||
|
if count > MaxCFHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many committed filter headers for "+
|
||||||
|
"message [count %v, max %v]", count,
|
||||||
|
MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgCFHeaders.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
msg.FilterHashes = make([]*chainhash.Hash, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
var cfh chainhash.Hash
|
||||||
|
err := readElement(r, &cfh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddCFHash(&cfh)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
// Write filter type
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write stop hash
|
||||||
|
err = writeElement(w, msg.StopHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write prev filter header
|
||||||
|
err = writeElement(w, msg.PrevFilterHeader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max committed headers per message.
|
||||||
|
count := len(msg.FilterHashes)
|
||||||
|
if count > MaxCFHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many committed filter headers for "+
|
||||||
|
"message [count %v, max %v]", count,
|
||||||
|
MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgCFHeaders.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cfh := range msg.FilterHashes {
|
||||||
|
err := writeElement(w, cfh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a filter header from r into the receiver using a format
|
||||||
|
// that is suitable for long-term storage such as a database. This function
|
||||||
|
// differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
|
||||||
|
// protocol as it was sent across the network. The wire encoding can
|
||||||
|
// technically differ depending on the protocol version and doesn't even really
|
||||||
|
// need to match the format of a stored filter header at all. As of the time
|
||||||
|
// this comment was written, the encoded filter header is the same in both
|
||||||
|
// instances, but there is a distinct difference and separating the two allows
|
||||||
|
// the API to be flexible enough to deal with changes.
|
||||||
|
func (msg *MsgCFHeaders) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// and the stable long-term storage format. As a result, make use of
|
||||||
|
// BtcDecode.
|
||||||
|
return msg.BtcDecode(r, 0, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgCFHeaders) Command() string {
|
||||||
|
return CmdCFHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Hash size + filter type + num headers (varInt) +
|
||||||
|
// (header size * max headers).
|
||||||
|
return 1 + chainhash.HashSize + chainhash.HashSize + MaxVarIntPayload +
|
||||||
|
(MaxCFHeaderPayload * MaxCFHeadersPerMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgCFHeaders returns a new bitcoin cfheaders message that conforms to
|
||||||
|
// the Message interface. See MsgCFHeaders for details.
|
||||||
|
func NewMsgCFHeaders() *MsgCFHeaders {
|
||||||
|
return &MsgCFHeaders{
|
||||||
|
FilterHashes: make([]*chainhash.Hash, 0, MaxCFHeadersPerMsg),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilterType is used to represent a filter type.
|
||||||
|
type FilterType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GCSFilterRegular is the regular filter type.
|
||||||
|
GCSFilterRegular FilterType = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxCFilterDataSize is the maximum byte size of a committed filter.
|
||||||
|
// The maximum size is currently defined as 256KiB.
|
||||||
|
MaxCFilterDataSize = 256 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgCFilter implements the Message interface and represents a bitcoin cfilter
|
||||||
|
// message. It is used to deliver a committed filter in response to a
|
||||||
|
// getcfilters (MsgGetCFilters) message.
|
||||||
|
type MsgCFilter struct {
|
||||||
|
FilterType FilterType
|
||||||
|
BlockHash chainhash.Hash
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFilter) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
// Read filter type
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the hash of the filter's block
|
||||||
|
err = readElement(r, &msg.BlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read filter data
|
||||||
|
msg.Data, err = ReadVarBytes(r, pver, MaxCFilterDataSize,
|
||||||
|
"cfilter data")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFilter) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
size := len(msg.Data)
|
||||||
|
if size > MaxCFilterDataSize {
|
||||||
|
str := fmt.Sprintf("cfilter size too large for message "+
|
||||||
|
"[size %v, max %v]", size, MaxCFilterDataSize)
|
||||||
|
return messageError("MsgCFilter.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, msg.BlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteVarBytes(w, pver, msg.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize decodes a filter from r into the receiver using a format that is
|
||||||
|
// suitable for long-term storage such as a database. This function differs
|
||||||
|
// from BtcDecode in that BtcDecode decodes from the bitcoin wire protocol as
|
||||||
|
// it was sent across the network. The wire encoding can technically differ
|
||||||
|
// depending on the protocol version and doesn't even really need to match the
|
||||||
|
// format of a stored filter at all. As of the time this comment was written,
|
||||||
|
// the encoded filter is the same in both instances, but there is a distinct
|
||||||
|
// difference and separating the two allows the API to be flexible enough to
|
||||||
|
// deal with changes.
|
||||||
|
func (msg *MsgCFilter) Deserialize(r io.Reader) error {
|
||||||
|
// At the current time, there is no difference between the wire encoding
|
||||||
|
// and the stable long-term storage format. As a result, make use of
|
||||||
|
// BtcDecode.
|
||||||
|
return msg.BtcDecode(r, 0, BaseEncoding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgCFilter) Command() string {
|
||||||
|
return CmdCFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgCFilter) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return uint32(VarIntSerializeSize(MaxCFilterDataSize)) +
|
||||||
|
MaxCFilterDataSize + chainhash.HashSize + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgCFilter returns a new bitcoin cfilter message that conforms to the
|
||||||
|
// Message interface. See MsgCFilter for details.
|
||||||
|
func NewMsgCFilter(filterType FilterType, blockHash *chainhash.Hash,
|
||||||
|
data []byte) *MsgCFilter {
|
||||||
|
return &MsgCFilter{
|
||||||
|
FilterType: filterType,
|
||||||
|
BlockHash: *blockHash,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgFeeFilter implements the Message interface and represents a bitcoin
|
||||||
|
// feefilter message. It is used to request the receiving peer does not
|
||||||
|
// announce any transactions below the specified minimum fee rate.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol versions starting with
|
||||||
|
// FeeFilterVersion.
|
||||||
|
type MsgFeeFilter struct {
|
||||||
|
MinFee int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFeeFilter) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < FeeFilterVersion {
|
||||||
|
str := fmt.Sprintf("feefilter message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFeeFilter.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.MinFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFeeFilter) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < FeeFilterVersion {
|
||||||
|
str := fmt.Sprintf("feefilter message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFeeFilter.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, msg.MinFee)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgFeeFilter) Command() string {
|
||||||
|
return CmdFeeFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFeeFilter) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgFeeFilter returns a new bitcoin feefilter message that conforms to
|
||||||
|
// the Message interface. See MsgFeeFilter for details.
|
||||||
|
func NewMsgFeeFilter(minfee int64) *MsgFeeFilter {
|
||||||
|
return &MsgFeeFilter{
|
||||||
|
MinFee: minfee,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxFilterAddDataSize is the maximum byte size of a data
|
||||||
|
// element to add to the Bloom filter. It is equal to the
|
||||||
|
// maximum element size of a script.
|
||||||
|
MaxFilterAddDataSize = 520
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgFilterAdd implements the Message interface and represents a bitcoin
|
||||||
|
// filteradd message. It is used to add a data element to an existing Bloom
|
||||||
|
// filter.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol version BIP0037Version.
|
||||||
|
type MsgFilterAdd struct {
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterAdd) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filteradd message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterAdd.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
msg.Data, err = ReadVarBytes(r, pver, MaxFilterAddDataSize,
|
||||||
|
"filteradd data")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterAdd) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filteradd message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterAdd.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := len(msg.Data)
|
||||||
|
if size > MaxFilterAddDataSize {
|
||||||
|
str := fmt.Sprintf("filteradd size too large for message "+
|
||||||
|
"[size %v, max %v]", size, MaxFilterAddDataSize)
|
||||||
|
return messageError("MsgFilterAdd.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteVarBytes(w, pver, msg.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterAdd) Command() string {
|
||||||
|
return CmdFilterAdd
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterAdd) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return uint32(VarIntSerializeSize(MaxFilterAddDataSize)) +
|
||||||
|
MaxFilterAddDataSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgFilterAdd returns a new bitcoin filteradd message that conforms to the
|
||||||
|
// Message interface. See MsgFilterAdd for details.
|
||||||
|
func NewMsgFilterAdd(data []byte) *MsgFilterAdd {
|
||||||
|
return &MsgFilterAdd{
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgFilterClear implements the Message interface and represents a bitcoin
|
||||||
|
// filterclear message which is used to reset a Bloom filter.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol version BIP0037Version and has
|
||||||
|
// no payload.
|
||||||
|
type MsgFilterClear struct{}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterClear) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filterclear message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterClear.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterClear) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filterclear message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterClear.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterClear) Command() string {
|
||||||
|
return CmdFilterClear
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterClear) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgFilterClear returns a new bitcoin filterclear message that conforms to the Message
|
||||||
|
// interface. See MsgFilterClear for details.
|
||||||
|
func NewMsgFilterClear() *MsgFilterClear {
|
||||||
|
return &MsgFilterClear{}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BloomUpdateType specifies how the filter is updated when a match is found
|
||||||
|
type BloomUpdateType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// BloomUpdateNone indicates the filter is not adjusted when a match is
|
||||||
|
// found.
|
||||||
|
BloomUpdateNone BloomUpdateType = 0
|
||||||
|
|
||||||
|
// BloomUpdateAll indicates if the filter matches any data element in a
|
||||||
|
// public key script, the outpoint is serialized and inserted into the
|
||||||
|
// filter.
|
||||||
|
BloomUpdateAll BloomUpdateType = 1
|
||||||
|
|
||||||
|
// BloomUpdateP2PubkeyOnly indicates if the filter matches a data
|
||||||
|
// element in a public key script and the script is of the standard
|
||||||
|
// pay-to-pubkey or multisig, the outpoint is serialized and inserted
|
||||||
|
// into the filter.
|
||||||
|
BloomUpdateP2PubkeyOnly BloomUpdateType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MaxFilterLoadHashFuncs is the maximum number of hash functions to
|
||||||
|
// load into the Bloom filter.
|
||||||
|
MaxFilterLoadHashFuncs = 50
|
||||||
|
|
||||||
|
// MaxFilterLoadFilterSize is the maximum size in bytes a filter may be.
|
||||||
|
MaxFilterLoadFilterSize = 36000
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgFilterLoad implements the Message interface and represents a bitcoin
|
||||||
|
// filterload message which is used to reset a Bloom filter.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol version BIP0037Version.
|
||||||
|
type MsgFilterLoad struct {
|
||||||
|
Filter []byte
|
||||||
|
HashFuncs uint32
|
||||||
|
Tweak uint32
|
||||||
|
Flags BloomUpdateType
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterLoad) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filterload message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterLoad.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
msg.Filter, err = ReadVarBytes(r, pver, MaxFilterLoadFilterSize,
|
||||||
|
"filterload filter size")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElements(r, &msg.HashFuncs, &msg.Tweak, &msg.Flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.HashFuncs > MaxFilterLoadHashFuncs {
|
||||||
|
str := fmt.Sprintf("too many filter hash functions for message "+
|
||||||
|
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
|
||||||
|
return messageError("MsgFilterLoad.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterLoad) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("filterload message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgFilterLoad.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
size := len(msg.Filter)
|
||||||
|
if size > MaxFilterLoadFilterSize {
|
||||||
|
str := fmt.Sprintf("filterload filter size too large for message "+
|
||||||
|
"[size %v, max %v]", size, MaxFilterLoadFilterSize)
|
||||||
|
return messageError("MsgFilterLoad.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.HashFuncs > MaxFilterLoadHashFuncs {
|
||||||
|
str := fmt.Sprintf("too many filter hash functions for message "+
|
||||||
|
"[count %v, max %v]", msg.HashFuncs, MaxFilterLoadHashFuncs)
|
||||||
|
return messageError("MsgFilterLoad.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarBytes(w, pver, msg.Filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElements(w, msg.HashFuncs, msg.Tweak, msg.Flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterLoad) Command() string {
|
||||||
|
return CmdFilterLoad
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgFilterLoad) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Num filter bytes (varInt) + filter + 4 bytes hash funcs +
|
||||||
|
// 4 bytes tweak + 1 byte flags.
|
||||||
|
return uint32(VarIntSerializeSize(MaxFilterLoadFilterSize)) +
|
||||||
|
MaxFilterLoadFilterSize + 9
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgFilterLoad returns a new bitcoin filterload message that conforms to
|
||||||
|
// the Message interface. See MsgFilterLoad for details.
|
||||||
|
func NewMsgFilterLoad(filter []byte, hashFuncs uint32, tweak uint32, flags BloomUpdateType) *MsgFilterLoad {
|
||||||
|
return &MsgFilterLoad{
|
||||||
|
Filter: filter,
|
||||||
|
HashFuncs: hashFuncs,
|
||||||
|
Tweak: tweak,
|
||||||
|
Flags: flags,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetAddr implements the Message interface and represents a bitcoin
|
||||||
|
// getaddr message. It is used to request a list of known active peers on the
|
||||||
|
// network from a peer to help identify potential nodes. The list is returned
|
||||||
|
// via one or more addr messages (MsgAddr).
|
||||||
|
//
|
||||||
|
// This message has no payload.
|
||||||
|
type MsgGetAddr struct{}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetAddr) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetAddr) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetAddr) Command() string {
|
||||||
|
return CmdGetAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetAddr) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetAddr returns a new bitcoin getaddr message that conforms to the
|
||||||
|
// Message interface. See MsgGetAddr for details.
|
||||||
|
func NewMsgGetAddr() *MsgGetAddr {
|
||||||
|
return &MsgGetAddr{}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxBlockLocatorsPerMsg is the maximum number of block locator hashes allowed
|
||||||
|
// per message.
|
||||||
|
const MaxBlockLocatorsPerMsg = 500
|
||||||
|
|
||||||
|
// MsgGetBlocks implements the Message interface and represents a bitcoin
|
||||||
|
// getblocks message. It is used to request a list of blocks starting after the
|
||||||
|
// last known hash in the slice of block locator hashes. The list is returned
|
||||||
|
// via an inv message (MsgInv) and is limited by a specific hash to stop at or
|
||||||
|
// the maximum number of blocks per message, which is currently 500.
|
||||||
|
//
|
||||||
|
// Set the HashStop field to the hash at which to stop and use
|
||||||
|
// AddBlockLocatorHash to build up the list of block locator hashes.
|
||||||
|
//
|
||||||
|
// The algorithm for building the block locator hashes should be to add the
|
||||||
|
// hashes in reverse order until you reach the genesis block. In order to keep
|
||||||
|
// the list of locator hashes to a reasonable number of entries, first add the
|
||||||
|
// most recent 10 block hashes, then double the step each loop iteration to
|
||||||
|
// exponentially decrease the number of hashes the further away from head and
|
||||||
|
// closer to the genesis block you get.
|
||||||
|
type MsgGetBlocks struct {
|
||||||
|
ProtocolVersion uint32
|
||||||
|
BlockLocatorHashes []*chainhash.Hash
|
||||||
|
HashStop chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlockLocatorHash adds a new block locator hash to the message.
|
||||||
|
func (msg *MsgGetBlocks) AddBlockLocatorHash(hash *chainhash.Hash) error {
|
||||||
|
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message [max %v]",
|
||||||
|
MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetBlocks.AddBlockLocatorHash", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlocks) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
err := readElement(r, &msg.ProtocolVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read num block locator hashes and limit to max.
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetBlocks.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
locatorHashes := make([]chainhash.Hash, count)
|
||||||
|
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
hash := &locatorHashes[i]
|
||||||
|
err := readElement(r, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddBlockLocatorHash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.HashStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlocks) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
count := len(msg.BlockLocatorHashes)
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetBlocks.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeElement(w, msg.ProtocolVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hash := range msg.BlockLocatorHashes {
|
||||||
|
err = writeElement(w, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, &msg.HashStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlocks) Command() string {
|
||||||
|
return CmdGetBlocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Protocol version 4 bytes + num hashes (varInt) + max block locator
|
||||||
|
// hashes + hash stop.
|
||||||
|
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg * chainhash.HashSize) + chainhash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetBlocks returns a new bitcoin getblocks message that conforms to the
|
||||||
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
|
// fields.
|
||||||
|
func NewMsgGetBlocks(hashStop *chainhash.Hash) *MsgGetBlocks {
|
||||||
|
return &MsgGetBlocks{
|
||||||
|
ProtocolVersion: ProtocolVersion,
|
||||||
|
BlockLocatorHashes: make([]*chainhash.Hash, 0, MaxBlockLocatorsPerMsg),
|
||||||
|
HashStop: *hashStop,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) 2018 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetCFCheckpt is a request for filter headers at evenly spaced intervals
|
||||||
|
// throughout the blockchain history. It allows to set the FilterType field to
|
||||||
|
// get headers in the chain of basic (0x00) or extended (0x01) headers.
|
||||||
|
type MsgGetCFCheckpt struct {
|
||||||
|
FilterType FilterType
|
||||||
|
StopHash chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFCheckpt) Command() string {
|
||||||
|
return CmdGetCFCheckpt
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFCheckpt) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Filter type + uint32 + block hash
|
||||||
|
return 1 + chainhash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetCFCheckpt returns a new bitcoin getcfcheckpt message that conforms
|
||||||
|
// to the Message interface using the passed parameters and defaults for the
|
||||||
|
// remaining fields.
|
||||||
|
func NewMsgGetCFCheckpt(filterType FilterType, stopHash *chainhash.Hash) *MsgGetCFCheckpt {
|
||||||
|
return &MsgGetCFCheckpt{
|
||||||
|
FilterType: filterType,
|
||||||
|
StopHash: *stopHash,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetCFHeaders is a message similar to MsgGetHeaders, but for committed
|
||||||
|
// filter headers. It allows to set the FilterType field to get headers in the
|
||||||
|
// chain of basic (0x00) or extended (0x01) headers.
|
||||||
|
type MsgGetCFHeaders struct {
|
||||||
|
FilterType FilterType
|
||||||
|
StartHeight uint32
|
||||||
|
StopHash chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElement(r, &msg.StartHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, &msg.StartHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFHeaders) Command() string {
|
||||||
|
return CmdGetCFHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Filter type + uint32 + block hash
|
||||||
|
return 1 + 4 + chainhash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetCFHeaders returns a new bitcoin getcfheader message that conforms to
|
||||||
|
// the Message interface using the passed parameters and defaults for the
|
||||||
|
// remaining fields.
|
||||||
|
func NewMsgGetCFHeaders(filterType FilterType, startHeight uint32,
|
||||||
|
stopHash *chainhash.Hash) *MsgGetCFHeaders {
|
||||||
|
return &MsgGetCFHeaders{
|
||||||
|
FilterType: filterType,
|
||||||
|
StartHeight: startHeight,
|
||||||
|
StopHash: *stopHash,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxGetCFiltersReqRange the maximum number of filters that may be requested in
|
||||||
|
// a getcfheaders message.
|
||||||
|
const MaxGetCFiltersReqRange = 1000
|
||||||
|
|
||||||
|
// MsgGetCFilters implements the Message interface and represents a bitcoin
|
||||||
|
// getcfilters message. It is used to request committed filters for a range of
|
||||||
|
// blocks.
|
||||||
|
type MsgGetCFilters struct {
|
||||||
|
FilterType FilterType
|
||||||
|
StartHeight uint32
|
||||||
|
StopHash chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFilters) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := readElement(r, &msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElement(r, &msg.StartHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFilters) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
|
||||||
|
err := writeElement(w, msg.FilterType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, &msg.StartHeight)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, &msg.StopHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFilters) Command() string {
|
||||||
|
return CmdGetCFilters
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetCFilters) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Filter type + uint32 + block hash
|
||||||
|
return 1 + 4 + chainhash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetCFilters returns a new bitcoin getcfilters message that conforms to
|
||||||
|
// the Message interface using the passed parameters and defaults for the
|
||||||
|
// remaining fields.
|
||||||
|
func NewMsgGetCFilters(filterType FilterType, startHeight uint32,
|
||||||
|
stopHash *chainhash.Hash) *MsgGetCFilters {
|
||||||
|
return &MsgGetCFilters{
|
||||||
|
FilterType: filterType,
|
||||||
|
StartHeight: startHeight,
|
||||||
|
StopHash: *stopHash,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetData implements the Message interface and represents a bitcoin
|
||||||
|
// getdata message. It is used to request data such as blocks and transactions
|
||||||
|
// from another peer. It should be used in response to the inv (MsgInv) message
|
||||||
|
// to request the actual data referenced by each inventory vector the receiving
|
||||||
|
// peer doesn't already have. Each message is limited to a maximum number of
|
||||||
|
// inventory vectors, which is currently 50,000. As a result, multiple messages
|
||||||
|
// must be used to request larger amounts of data.
|
||||||
|
//
|
||||||
|
// Use the AddInvVect function to build up the list of inventory vectors when
|
||||||
|
// sending a getdata message to another peer.
|
||||||
|
type MsgGetData struct {
|
||||||
|
InvList []*InvVect
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInvVect adds an inventory vector to the message.
|
||||||
|
func (msg *MsgGetData) AddInvVect(iv *InvVect) error {
|
||||||
|
if len(msg.InvList)+1 > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [max %v]",
|
||||||
|
MaxInvPerMsg)
|
||||||
|
return messageError("MsgGetData.AddInvVect", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.InvList = append(msg.InvList, iv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetData) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgGetData.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
iv := &invList[i]
|
||||||
|
err := readInvVect(r, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddInvVect(iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetData) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
count := len(msg.InvList)
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgGetData.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iv := range msg.InvList {
|
||||||
|
err := writeInvVect(w, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetData) Command() string {
|
||||||
|
return CmdGetData
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetData) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Num inventory vectors (varInt) + max allowed inventory vectors.
|
||||||
|
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetData returns a new bitcoin getdata message that conforms to the
|
||||||
|
// Message interface. See MsgGetData for details.
|
||||||
|
func NewMsgGetData() *MsgGetData {
|
||||||
|
return &MsgGetData{
|
||||||
|
InvList: make([]*InvVect, 0, defaultInvListAlloc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetDataSizeHint returns a new bitcoin getdata message that conforms to
|
||||||
|
// the Message interface. See MsgGetData for details. This function differs
|
||||||
|
// from NewMsgGetData in that it allows a default allocation size for the
|
||||||
|
// backing array which houses the inventory vector list. This allows callers
|
||||||
|
// who know in advance how large the inventory list will grow to avoid the
|
||||||
|
// overhead of growing the internal backing array several times when appending
|
||||||
|
// large amounts of inventory vectors with AddInvVect. Note that the specified
|
||||||
|
// hint is just that - a hint that is used for the default allocation size.
|
||||||
|
// Adding more (or less) inventory vectors will still work properly. The size
|
||||||
|
// hint is limited to MaxInvPerMsg.
|
||||||
|
func NewMsgGetDataSizeHint(sizeHint uint) *MsgGetData {
|
||||||
|
// Limit the specified hint to the maximum allow per message.
|
||||||
|
if sizeHint > MaxInvPerMsg {
|
||||||
|
sizeHint = MaxInvPerMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MsgGetData{
|
||||||
|
InvList: make([]*InvVect, 0, sizeHint),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgGetHeaders implements the Message interface and represents a bitcoin
|
||||||
|
// getheaders message. It is used to request a list of block headers for
|
||||||
|
// blocks starting after the last known hash in the slice of block locator
|
||||||
|
// hashes. The list is returned via a headers message (MsgHeaders) and is
|
||||||
|
// limited by a specific hash to stop at or the maximum number of block headers
|
||||||
|
// per message, which is currently 2000.
|
||||||
|
//
|
||||||
|
// Set the HashStop field to the hash at which to stop and use
|
||||||
|
// AddBlockLocatorHash to build up the list of block locator hashes.
|
||||||
|
//
|
||||||
|
// The algorithm for building the block locator hashes should be to add the
|
||||||
|
// hashes in reverse order until you reach the genesis block. In order to keep
|
||||||
|
// the list of locator hashes to a resonable number of entries, first add the
|
||||||
|
// most recent 10 block hashes, then double the step each loop iteration to
|
||||||
|
// exponentially decrease the number of hashes the further away from head and
|
||||||
|
// closer to the genesis block you get.
|
||||||
|
type MsgGetHeaders struct {
|
||||||
|
ProtocolVersion uint32
|
||||||
|
BlockLocatorHashes []*chainhash.Hash
|
||||||
|
HashStop chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlockLocatorHash adds a new block locator hash to the message.
|
||||||
|
func (msg *MsgGetHeaders) AddBlockLocatorHash(hash *chainhash.Hash) error {
|
||||||
|
if len(msg.BlockLocatorHashes)+1 > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message [max %v]",
|
||||||
|
MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetHeaders.AddBlockLocatorHash", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
err := readElement(r, &msg.ProtocolVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read num block locator hashes and limit to max.
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetHeaders.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
locatorHashes := make([]chainhash.Hash, count)
|
||||||
|
msg.BlockLocatorHashes = make([]*chainhash.Hash, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
hash := &locatorHashes[i]
|
||||||
|
err := readElement(r, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddBlockLocatorHash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.HashStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Limit to max block locator hashes per message.
|
||||||
|
count := len(msg.BlockLocatorHashes)
|
||||||
|
if count > MaxBlockLocatorsPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block locator hashes for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockLocatorsPerMsg)
|
||||||
|
return messageError("MsgGetHeaders.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeElement(w, msg.ProtocolVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hash := range msg.BlockLocatorHashes {
|
||||||
|
err := writeElement(w, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, &msg.HashStop)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgGetHeaders) Command() string {
|
||||||
|
return CmdGetHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Version 4 bytes + num block locator hashes (varInt) + max allowed block
|
||||||
|
// locators + hash stop.
|
||||||
|
return 4 + MaxVarIntPayload + (MaxBlockLocatorsPerMsg *
|
||||||
|
chainhash.HashSize) + chainhash.HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgGetHeaders returns a new bitcoin getheaders message that conforms to
|
||||||
|
// the Message interface. See MsgGetHeaders for details.
|
||||||
|
func NewMsgGetHeaders() *MsgGetHeaders {
|
||||||
|
return &MsgGetHeaders{
|
||||||
|
BlockLocatorHashes: make([]*chainhash.Hash, 0,
|
||||||
|
MaxBlockLocatorsPerMsg),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxBlockHeadersPerMsg is the maximum number of block headers that can be in
|
||||||
|
// a single bitcoin headers message.
|
||||||
|
const MaxBlockHeadersPerMsg = 2000
|
||||||
|
|
||||||
|
// MsgHeaders implements the Message interface and represents a bitcoin headers
|
||||||
|
// message. It is used to deliver block header information in response
|
||||||
|
// to a getheaders message (MsgGetHeaders). The maximum number of block headers
|
||||||
|
// per message is currently 2000. See MsgGetHeaders for details on requesting
|
||||||
|
// the headers.
|
||||||
|
type MsgHeaders struct {
|
||||||
|
Headers []*BlockHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBlockHeader adds a new block header to the message.
|
||||||
|
func (msg *MsgHeaders) AddBlockHeader(bh *BlockHeader) error {
|
||||||
|
if len(msg.Headers)+1 > MaxBlockHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block headers in message [max %v]",
|
||||||
|
MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgHeaders.AddBlockHeader", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Headers = append(msg.Headers, bh)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max block headers per message.
|
||||||
|
if count > MaxBlockHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block headers for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgHeaders.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of headers to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
headers := make([]BlockHeader, count)
|
||||||
|
msg.Headers = make([]*BlockHeader, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
bh := &headers[i]
|
||||||
|
err := readBlockHeader(r, pver, bh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
txCount, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the transaction count is zero for headers.
|
||||||
|
if txCount > 0 {
|
||||||
|
str := fmt.Sprintf("block headers may not contain "+
|
||||||
|
"transactions [count %v]", txCount)
|
||||||
|
return messageError("MsgHeaders.BtcDecode", str)
|
||||||
|
}
|
||||||
|
msg.AddBlockHeader(bh)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Limit to max block headers per message.
|
||||||
|
count := len(msg.Headers)
|
||||||
|
if count > MaxBlockHeadersPerMsg {
|
||||||
|
str := fmt.Sprintf("too many block headers for message "+
|
||||||
|
"[count %v, max %v]", count, MaxBlockHeadersPerMsg)
|
||||||
|
return messageError("MsgHeaders.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bh := range msg.Headers {
|
||||||
|
err := writeBlockHeader(w, pver, bh)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The wire protocol encoding always includes a 0 for the number
|
||||||
|
// of transactions on header messages. This is really just an
|
||||||
|
// artifact of the way the original implementation serializes
|
||||||
|
// block headers, but it is required.
|
||||||
|
err = WriteVarInt(w, pver, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgHeaders) Command() string {
|
||||||
|
return CmdHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Num headers (varInt) + max allowed headers (header length + 1 byte
|
||||||
|
// for the number of transactions which is always 0).
|
||||||
|
return MaxVarIntPayload + ((MaxBlockHeaderPayload + 1) *
|
||||||
|
MaxBlockHeadersPerMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgHeaders returns a new bitcoin headers message that conforms to the
|
||||||
|
// Message interface. See MsgHeaders for details.
|
||||||
|
func NewMsgHeaders() *MsgHeaders {
|
||||||
|
return &MsgHeaders{
|
||||||
|
Headers: make([]*BlockHeader, 0, MaxBlockHeadersPerMsg),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultInvListAlloc is the default size used for the backing array for an
|
||||||
|
// inventory list. The array will dynamically grow as needed, but this
|
||||||
|
// figure is intended to provide enough space for the max number of inventory
|
||||||
|
// vectors in a *typical* inventory message without needing to grow the backing
|
||||||
|
// array multiple times. Technically, the list can grow to MaxInvPerMsg, but
|
||||||
|
// rather than using that large figure, this figure more accurately reflects the
|
||||||
|
// typical case.
|
||||||
|
const defaultInvListAlloc = 1000
|
||||||
|
|
||||||
|
// MsgInv implements the Message interface and represents a bitcoin inv message.
|
||||||
|
// It is used to advertise a peer's known data such as blocks and transactions
|
||||||
|
// through inventory vectors. It may be sent unsolicited to inform other peers
|
||||||
|
// of the data or in response to a getblocks message (MsgGetBlocks). Each
|
||||||
|
// message is limited to a maximum number of inventory vectors, which is
|
||||||
|
// currently 50,000.
|
||||||
|
//
|
||||||
|
// Use the AddInvVect function to build up the list of inventory vectors when
|
||||||
|
// sending an inv message to another peer.
|
||||||
|
type MsgInv struct {
|
||||||
|
InvList []*InvVect
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInvVect adds an inventory vector to the message.
|
||||||
|
func (msg *MsgInv) AddInvVect(iv *InvVect) error {
|
||||||
|
if len(msg.InvList)+1 > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [max %v]",
|
||||||
|
MaxInvPerMsg)
|
||||||
|
return messageError("MsgInv.AddInvVect", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.InvList = append(msg.InvList, iv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgInv) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgInv.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
iv := &invList[i]
|
||||||
|
err := readInvVect(r, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddInvVect(iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgInv) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
count := len(msg.InvList)
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgInv.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iv := range msg.InvList {
|
||||||
|
err := writeInvVect(w, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgInv) Command() string {
|
||||||
|
return CmdInv
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgInv) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Num inventory vectors (varInt) + max allowed inventory vectors.
|
||||||
|
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgInv returns a new bitcoin inv message that conforms to the Message
|
||||||
|
// interface. See MsgInv for details.
|
||||||
|
func NewMsgInv() *MsgInv {
|
||||||
|
return &MsgInv{
|
||||||
|
InvList: make([]*InvVect, 0, defaultInvListAlloc),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgInvSizeHint returns a new bitcoin inv message that conforms to the
|
||||||
|
// Message interface. See MsgInv for details. This function differs from
|
||||||
|
// NewMsgInv in that it allows a default allocation size for the backing array
|
||||||
|
// which houses the inventory vector list. This allows callers who know in
|
||||||
|
// advance how large the inventory list will grow to avoid the overhead of
|
||||||
|
// growing the internal backing array several times when appending large amounts
|
||||||
|
// of inventory vectors with AddInvVect. Note that the specified hint is just
|
||||||
|
// that - a hint that is used for the default allocation size. Adding more
|
||||||
|
// (or less) inventory vectors will still work properly. The size hint is
|
||||||
|
// limited to MaxInvPerMsg.
|
||||||
|
func NewMsgInvSizeHint(sizeHint uint) *MsgInv {
|
||||||
|
// Limit the specified hint to the maximum allow per message.
|
||||||
|
if sizeHint > MaxInvPerMsg {
|
||||||
|
sizeHint = MaxInvPerMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MsgInv{
|
||||||
|
InvList: make([]*InvVect, 0, sizeHint),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgMemPool implements the Message interface and represents a bitcoin mempool
|
||||||
|
// message. It is used to request a list of transactions still in the active
|
||||||
|
// memory pool of a relay.
|
||||||
|
//
|
||||||
|
// This message has no payload and was not added until protocol versions
|
||||||
|
// starting with BIP0035Version.
|
||||||
|
type MsgMemPool struct{}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMemPool) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0035Version {
|
||||||
|
str := fmt.Sprintf("mempool message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgMemPool.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMemPool) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0035Version {
|
||||||
|
str := fmt.Sprintf("mempool message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgMemPool.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgMemPool) Command() string {
|
||||||
|
return CmdMemPool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMemPool) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgMemPool returns a new bitcoin pong message that conforms to the Message
|
||||||
|
// interface. See MsgPong for details.
|
||||||
|
func NewMsgMemPool() *MsgMemPool {
|
||||||
|
return &MsgMemPool{}
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxFlagsPerMerkleBlock is the maximum number of flag bytes that could
|
||||||
|
// possibly fit into a merkle block. Since each transaction is represented by
|
||||||
|
// a single bit, this is the max number of transactions per block divided by
|
||||||
|
// 8 bits per byte. Then an extra one to cover partials.
|
||||||
|
const maxFlagsPerMerkleBlock = maxTxPerBlock / 8
|
||||||
|
|
||||||
|
// MsgMerkleBlock implements the Message interface and represents a bitcoin
|
||||||
|
// merkleblock message which is used to reset a Bloom filter.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol version BIP0037Version.
|
||||||
|
type MsgMerkleBlock struct {
|
||||||
|
Header BlockHeader
|
||||||
|
Transactions uint32
|
||||||
|
Hashes []*chainhash.Hash
|
||||||
|
Flags []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTxHash adds a new transaction hash to the message.
|
||||||
|
func (msg *MsgMerkleBlock) AddTxHash(hash *chainhash.Hash) error {
|
||||||
|
if len(msg.Hashes)+1 > maxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many tx hashes for message [max %v]",
|
||||||
|
maxTxPerBlock)
|
||||||
|
return messageError("MsgMerkleBlock.AddTxHash", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Hashes = append(msg.Hashes, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMerkleBlock) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("merkleblock message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := readBlockHeader(r, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readElement(r, &msg.Transactions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read num block locator hashes and limit to max.
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if count > maxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transaction hashes for message "+
|
||||||
|
"[count %v, max %v]", count, maxTxPerBlock)
|
||||||
|
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of hashes to deserialize into in order to
|
||||||
|
// reduce the number of allocations.
|
||||||
|
hashes := make([]chainhash.Hash, count)
|
||||||
|
msg.Hashes = make([]*chainhash.Hash, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
hash := &hashes[i]
|
||||||
|
err := readElement(r, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddTxHash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Flags, err = ReadVarBytes(r, pver, maxFlagsPerMerkleBlock,
|
||||||
|
"merkle block flags size")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMerkleBlock) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < BIP0037Version {
|
||||||
|
str := fmt.Sprintf("merkleblock message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgMerkleBlock.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read num transaction hashes and limit to max.
|
||||||
|
numHashes := len(msg.Hashes)
|
||||||
|
if numHashes > maxTxPerBlock {
|
||||||
|
str := fmt.Sprintf("too many transaction hashes for message "+
|
||||||
|
"[count %v, max %v]", numHashes, maxTxPerBlock)
|
||||||
|
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||||
|
}
|
||||||
|
numFlagBytes := len(msg.Flags)
|
||||||
|
if numFlagBytes > maxFlagsPerMerkleBlock {
|
||||||
|
str := fmt.Sprintf("too many flag bytes for message [count %v, "+
|
||||||
|
"max %v]", numFlagBytes, maxFlagsPerMerkleBlock)
|
||||||
|
return messageError("MsgMerkleBlock.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeBlockHeader(w, pver, &msg.Header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, msg.Transactions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarInt(w, pver, uint64(numHashes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, hash := range msg.Hashes {
|
||||||
|
err = writeElement(w, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteVarBytes(w, pver, msg.Flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgMerkleBlock) Command() string {
|
||||||
|
return CmdMerkleBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgMerkleBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return MaxBlockPayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgMerkleBlock returns a new bitcoin merkleblock message that conforms to
|
||||||
|
// the Message interface. See MsgMerkleBlock for details.
|
||||||
|
func NewMsgMerkleBlock(bh *BlockHeader) *MsgMerkleBlock {
|
||||||
|
return &MsgMerkleBlock{
|
||||||
|
Header: *bh,
|
||||||
|
Transactions: 0,
|
||||||
|
Hashes: make([]*chainhash.Hash, 0),
|
||||||
|
Flags: make([]byte, 0),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgNotFound defines a bitcoin notfound message which is sent in response to
|
||||||
|
// a getdata message if any of the requested data in not available on the peer.
|
||||||
|
// Each message is limited to a maximum number of inventory vectors, which is
|
||||||
|
// currently 50,000.
|
||||||
|
//
|
||||||
|
// Use the AddInvVect function to build up the list of inventory vectors when
|
||||||
|
// sending a notfound message to another peer.
|
||||||
|
type MsgNotFound struct {
|
||||||
|
InvList []*InvVect
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInvVect adds an inventory vector to the message.
|
||||||
|
func (msg *MsgNotFound) AddInvVect(iv *InvVect) error {
|
||||||
|
if len(msg.InvList)+1 > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [max %v]",
|
||||||
|
MaxInvPerMsg)
|
||||||
|
return messageError("MsgNotFound.AddInvVect", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.InvList = append(msg.InvList, iv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgNotFound) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
count, err := ReadVarInt(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgNotFound.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a contiguous slice of inventory vectors to deserialize into in
|
||||||
|
// order to reduce the number of allocations.
|
||||||
|
invList := make([]InvVect, count)
|
||||||
|
msg.InvList = make([]*InvVect, 0, count)
|
||||||
|
for i := uint64(0); i < count; i++ {
|
||||||
|
iv := &invList[i]
|
||||||
|
err := readInvVect(r, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.AddInvVect(iv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgNotFound) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// Limit to max inventory vectors per message.
|
||||||
|
count := len(msg.InvList)
|
||||||
|
if count > MaxInvPerMsg {
|
||||||
|
str := fmt.Sprintf("too many invvect in message [%v]", count)
|
||||||
|
return messageError("MsgNotFound.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := WriteVarInt(w, pver, uint64(count))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iv := range msg.InvList {
|
||||||
|
err := writeInvVect(w, pver, iv)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgNotFound) Command() string {
|
||||||
|
return CmdNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgNotFound) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// Max var int 9 bytes + max InvVects at 36 bytes each.
|
||||||
|
// Num inventory vectors (varInt) + max allowed inventory vectors.
|
||||||
|
return MaxVarIntPayload + (MaxInvPerMsg * maxInvVectPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgNotFound returns a new bitcoin notfound message that conforms to the
|
||||||
|
// Message interface. See MsgNotFound for details.
|
||||||
|
func NewMsgNotFound() *MsgNotFound {
|
||||||
|
return &MsgNotFound{
|
||||||
|
InvList: make([]*InvVect, 0, defaultInvListAlloc),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgPing implements the Message interface and represents a bitcoin ping
|
||||||
|
// message.
|
||||||
|
//
|
||||||
|
// For versions BIP0031Version and earlier, it is used primarily to confirm
|
||||||
|
// that a connection is still valid. A transmission error is typically
|
||||||
|
// interpreted as a closed connection and that the peer should be removed.
|
||||||
|
// For versions AFTER BIP0031Version it contains an identifier which can be
|
||||||
|
// returned in the pong message to determine network timing.
|
||||||
|
//
|
||||||
|
// The payload for this message just consists of a nonce used for identifying
|
||||||
|
// it later.
|
||||||
|
type MsgPing struct {
|
||||||
|
// Unique value associated with message that is used to identify
|
||||||
|
// specific ping message.
|
||||||
|
Nonce uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPing) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
// There was no nonce for BIP0031Version and earlier.
|
||||||
|
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver > BIP0031Version {
|
||||||
|
err := readElement(r, &msg.Nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPing) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// There was no nonce for BIP0031Version and earlier.
|
||||||
|
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver > BIP0031Version {
|
||||||
|
err := writeElement(w, msg.Nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgPing) Command() string {
|
||||||
|
return CmdPing
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPing) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
plen := uint32(0)
|
||||||
|
// There was no nonce for BIP0031Version and earlier.
|
||||||
|
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver > BIP0031Version {
|
||||||
|
// Nonce 8 bytes.
|
||||||
|
plen += 8
|
||||||
|
}
|
||||||
|
|
||||||
|
return plen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgPing returns a new bitcoin ping message that conforms to the Message
|
||||||
|
// interface. See MsgPing for details.
|
||||||
|
func NewMsgPing(nonce uint64) *MsgPing {
|
||||||
|
return &MsgPing{
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgPong implements the Message interface and represents a bitcoin pong
|
||||||
|
// message which is used primarily to confirm that a connection is still valid
|
||||||
|
// in response to a bitcoin ping message (MsgPing).
|
||||||
|
//
|
||||||
|
// This message was not added until protocol versions AFTER BIP0031Version.
|
||||||
|
type MsgPong struct {
|
||||||
|
// Unique value associated with message that is used to identify
|
||||||
|
// specific ping message.
|
||||||
|
Nonce uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPong) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
// NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver <= BIP0031Version {
|
||||||
|
str := fmt.Sprintf("pong message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgPong.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return readElement(r, &msg.Nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPong) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
// NOTE: <= is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver <= BIP0031Version {
|
||||||
|
str := fmt.Sprintf("pong message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgPong.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeElement(w, msg.Nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgPong) Command() string {
|
||||||
|
return CmdPong
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgPong) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
plen := uint32(0)
|
||||||
|
// The pong message did not exist for BIP0031Version and earlier.
|
||||||
|
// NOTE: > is not a mistake here. The BIP0031 was defined as AFTER
|
||||||
|
// the version unlike most others.
|
||||||
|
if pver > BIP0031Version {
|
||||||
|
// Nonce 8 bytes.
|
||||||
|
plen += 8
|
||||||
|
}
|
||||||
|
|
||||||
|
return plen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgPong returns a new bitcoin pong message that conforms to the Message
|
||||||
|
// interface. See MsgPong for details.
|
||||||
|
func NewMsgPong(nonce uint64) *MsgPong {
|
||||||
|
return &MsgPong{
|
||||||
|
Nonce: nonce,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RejectCode represents a numeric value by which a remote peer indicates
|
||||||
|
// why a message was rejected.
|
||||||
|
type RejectCode uint8
|
||||||
|
|
||||||
|
// These constants define the various supported reject codes.
|
||||||
|
const (
|
||||||
|
RejectMalformed RejectCode = 0x01
|
||||||
|
RejectInvalid RejectCode = 0x10
|
||||||
|
RejectObsolete RejectCode = 0x11
|
||||||
|
RejectDuplicate RejectCode = 0x12
|
||||||
|
RejectNonstandard RejectCode = 0x40
|
||||||
|
RejectDust RejectCode = 0x41
|
||||||
|
RejectInsufficientFee RejectCode = 0x42
|
||||||
|
RejectCheckpoint RejectCode = 0x43
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map of reject codes back strings for pretty printing.
|
||||||
|
var rejectCodeStrings = map[RejectCode]string{
|
||||||
|
RejectMalformed: "REJECT_MALFORMED",
|
||||||
|
RejectInvalid: "REJECT_INVALID",
|
||||||
|
RejectObsolete: "REJECT_OBSOLETE",
|
||||||
|
RejectDuplicate: "REJECT_DUPLICATE",
|
||||||
|
RejectNonstandard: "REJECT_NONSTANDARD",
|
||||||
|
RejectDust: "REJECT_DUST",
|
||||||
|
RejectInsufficientFee: "REJECT_INSUFFICIENTFEE",
|
||||||
|
RejectCheckpoint: "REJECT_CHECKPOINT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the RejectCode in human-readable form.
|
||||||
|
func (code RejectCode) String() string {
|
||||||
|
if s, ok := rejectCodeStrings[code]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsgReject implements the Message interface and represents a bitcoin reject
|
||||||
|
// message.
|
||||||
|
//
|
||||||
|
// This message was not added until protocol version RejectVersion.
|
||||||
|
type MsgReject struct {
|
||||||
|
// Cmd is the command for the message which was rejected such as
|
||||||
|
// as CmdBlock or CmdTx. This can be obtained from the Command function
|
||||||
|
// of a Message.
|
||||||
|
Cmd string
|
||||||
|
|
||||||
|
// RejectCode is a code indicating why the command was rejected. It
|
||||||
|
// is encoded as a uint8 on the wire.
|
||||||
|
Code RejectCode
|
||||||
|
|
||||||
|
// Reason is a human-readable string with specific details (over and
|
||||||
|
// above the reject code) about why the command was rejected.
|
||||||
|
Reason string
|
||||||
|
|
||||||
|
// Hash identifies a specific block or transaction that was rejected
|
||||||
|
// and therefore only applies the MsgBlock and MsgTx messages.
|
||||||
|
Hash chainhash.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < RejectVersion {
|
||||||
|
str := fmt.Sprintf("reject message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgReject.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command that was rejected.
|
||||||
|
cmd, err := ReadVarString(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Cmd = cmd
|
||||||
|
|
||||||
|
// Code indicating why the command was rejected.
|
||||||
|
err = readElement(r, &msg.Code)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Human readable string with specific details (over and above the
|
||||||
|
// reject code above) about why the command was rejected.
|
||||||
|
reason, err := ReadVarString(r, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Reason = reason
|
||||||
|
|
||||||
|
// CmdBlock and CmdTx messages have an additional hash field that
|
||||||
|
// identifies the specific block or transaction.
|
||||||
|
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
|
||||||
|
err := readElement(r, &msg.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < RejectVersion {
|
||||||
|
str := fmt.Sprintf("reject message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgReject.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command that was rejected.
|
||||||
|
err := WriteVarString(w, pver, msg.Cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code indicating why the command was rejected.
|
||||||
|
err = writeElement(w, msg.Code)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Human readable string with specific details (over and above the
|
||||||
|
// reject code above) about why the command was rejected.
|
||||||
|
err = WriteVarString(w, pver, msg.Reason)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CmdBlock and CmdTx messages have an additional hash field that
|
||||||
|
// identifies the specific block or transaction.
|
||||||
|
if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
|
||||||
|
err := writeElement(w, &msg.Hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgReject) Command() string {
|
||||||
|
return CmdReject
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgReject) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
plen := uint32(0)
|
||||||
|
// The reject message did not exist before protocol version
|
||||||
|
// RejectVersion.
|
||||||
|
if pver >= RejectVersion {
|
||||||
|
// Unfortunately the bitcoin protocol does not enforce a sane
|
||||||
|
// limit on the length of the reason, so the max payload is the
|
||||||
|
// overall maximum message payload.
|
||||||
|
plen = MaxMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
return plen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgReject returns a new bitcoin reject message that conforms to the
|
||||||
|
// Message interface. See MsgReject for details.
|
||||||
|
func NewMsgReject(command string, code RejectCode, reason string) *MsgReject {
|
||||||
|
return &MsgReject{
|
||||||
|
Cmd: command,
|
||||||
|
Code: code,
|
||||||
|
Reason: reason,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgSendHeaders implements the Message interface and represents a bitcoin
|
||||||
|
// sendheaders message. It is used to request the peer send block headers
|
||||||
|
// rather than inventory vectors.
|
||||||
|
//
|
||||||
|
// This message has no payload and was not added until protocol versions
|
||||||
|
// starting with SendHeadersVersion.
|
||||||
|
type MsgSendHeaders struct{}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgSendHeaders) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < SendHeadersVersion {
|
||||||
|
str := fmt.Sprintf("sendheaders message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgSendHeaders.BtcDecode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgSendHeaders) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
if pver < SendHeadersVersion {
|
||||||
|
str := fmt.Sprintf("sendheaders message invalid for protocol "+
|
||||||
|
"version %d", pver)
|
||||||
|
return messageError("MsgSendHeaders.BtcEncode", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgSendHeaders) Command() string {
|
||||||
|
return CmdSendHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgSendHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgSendHeaders returns a new bitcoin sendheaders message that conforms to
|
||||||
|
// the Message interface. See MsgSendHeaders for details.
|
||||||
|
func NewMsgSendHeaders() *MsgSendHeaders {
|
||||||
|
return &MsgSendHeaders{}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MsgVerAck defines a bitcoin verack message which is used for a peer to
|
||||||
|
// acknowledge a version message (MsgVersion) after it has used the information
|
||||||
|
// to negotiate parameters. It implements the Message interface.
|
||||||
|
//
|
||||||
|
// This message has no payload.
|
||||||
|
type MsgVerAck struct{}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVerAck) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVerAck) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgVerAck) Command() string {
|
||||||
|
return CmdVerAck
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVerAck) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgVerAck returns a new bitcoin verack message that conforms to the
|
||||||
|
// Message interface.
|
||||||
|
func NewMsgVerAck() *MsgVerAck {
|
||||||
|
return &MsgVerAck{}
|
||||||
|
}
|
|
@ -0,0 +1,269 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
|
||||||
|
// version message (MsgVersion).
|
||||||
|
const MaxUserAgentLen = 256
|
||||||
|
|
||||||
|
// DefaultUserAgent for wire in the stack
|
||||||
|
const DefaultUserAgent = "/btcwire:0.5.0/"
|
||||||
|
|
||||||
|
// MsgVersion implements the Message interface and represents a bitcoin version
|
||||||
|
// message. It is used for a peer to advertise itself as soon as an outbound
|
||||||
|
// connection is made. The remote peer then uses this information along with
|
||||||
|
// its own to negotiate. The remote peer must then respond with a version
|
||||||
|
// message of its own containing the negotiated values followed by a verack
|
||||||
|
// message (MsgVerAck). This exchange must take place before any further
|
||||||
|
// communication is allowed to proceed.
|
||||||
|
type MsgVersion struct {
|
||||||
|
// Version of the protocol the node is using.
|
||||||
|
ProtocolVersion int32
|
||||||
|
|
||||||
|
// Bitfield which identifies the enabled services.
|
||||||
|
Services ServiceFlag
|
||||||
|
|
||||||
|
// Time the message was generated. This is encoded as an int64 on the wire.
|
||||||
|
Timestamp time.Time
|
||||||
|
|
||||||
|
// Address of the remote peer.
|
||||||
|
AddrYou NetAddress
|
||||||
|
|
||||||
|
// Address of the local peer.
|
||||||
|
AddrMe NetAddress
|
||||||
|
|
||||||
|
// Unique value associated with message that is used to detect self
|
||||||
|
// connections.
|
||||||
|
Nonce uint64
|
||||||
|
|
||||||
|
// The user agent that generated messsage. This is a encoded as a varString
|
||||||
|
// on the wire. This has a max length of MaxUserAgentLen.
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
// Last block seen by the generator of the version message.
|
||||||
|
LastBlock int32
|
||||||
|
|
||||||
|
// Don't announce transactions to peer.
|
||||||
|
DisableRelayTx bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasService returns whether the specified service is supported by the peer
|
||||||
|
// that generated the message.
|
||||||
|
func (msg *MsgVersion) HasService(service ServiceFlag) bool {
|
||||||
|
return msg.Services&service == service
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddService adds service as a supported service by the peer generating the
|
||||||
|
// message.
|
||||||
|
func (msg *MsgVersion) AddService(service ServiceFlag) {
|
||||||
|
msg.Services |= service
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
|
||||||
|
// The version message is special in that the protocol version hasn't been
|
||||||
|
// negotiated yet. As a result, the pver field is ignored and any fields which
|
||||||
|
// are added in new versions are optional. This also mean that r must be a
|
||||||
|
// *bytes.Buffer so the number of remaining bytes can be ascertained.
|
||||||
|
//
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
|
||||||
|
buf, ok := r.(*bytes.Buffer)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("MsgVersion.BtcDecode reader is not a " +
|
||||||
|
"*bytes.Buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := readElements(buf, &msg.ProtocolVersion, &msg.Services,
|
||||||
|
(*int64Time)(&msg.Timestamp))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = readNetAddress(buf, pver, &msg.AddrYou, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol versions >= 106 added a from address, nonce, and user agent
|
||||||
|
// field and they are only considered present if there are bytes
|
||||||
|
// remaining in the message.
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
err = readNetAddress(buf, pver, &msg.AddrMe, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
err = readElement(buf, &msg.Nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
userAgent, err := ReadVarString(buf, pver)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = validateUserAgent(userAgent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.UserAgent = userAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol versions >= 209 added a last known block field. It is only
|
||||||
|
// considered present if there are bytes remaining in the message.
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
err = readElement(buf, &msg.LastBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There was no relay transactions field before BIP0037Version, but
|
||||||
|
// the default behavior prior to the addition of the field was to always
|
||||||
|
// relay transactions.
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
// It's safe to ignore the error here since the buffer has at
|
||||||
|
// least one byte and that byte will result in a boolean value
|
||||||
|
// regardless of its value. Also, the wire encoding for the
|
||||||
|
// field is true when transactions should be relayed, so reverse
|
||||||
|
// it for the DisableRelayTx field.
|
||||||
|
var relayTx bool
|
||||||
|
readElement(r, &relayTx)
|
||||||
|
msg.DisableRelayTx = !relayTx
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
|
||||||
|
err := validateUserAgent(msg.UserAgent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElements(w, msg.ProtocolVersion, msg.Services,
|
||||||
|
msg.Timestamp.Unix())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeNetAddress(w, pver, &msg.AddrYou, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeNetAddress(w, pver, &msg.AddrMe, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, msg.Nonce)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = WriteVarString(w, pver, msg.UserAgent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeElement(w, msg.LastBlock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// There was no relay transactions field before BIP0037Version. Also,
|
||||||
|
// the wire encoding for the field is true when transactions should be
|
||||||
|
// relayed, so reverse it from the DisableRelayTx field.
|
||||||
|
if pver >= BIP0037Version {
|
||||||
|
err = writeElement(w, !msg.DisableRelayTx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgVersion) Command() string {
|
||||||
|
return CmdVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
// XXX: <= 106 different
|
||||||
|
|
||||||
|
// Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes +
|
||||||
|
// remote and local net addresses + nonce 8 bytes + length of user
|
||||||
|
// agent (varInt) + max allowed useragent length + last block 4 bytes +
|
||||||
|
// relay transactions flag 1 byte.
|
||||||
|
return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload +
|
||||||
|
MaxUserAgentLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgVersion returns a new bitcoin version message that conforms to the
|
||||||
|
// Message interface using the passed parameters and defaults for the remaining
|
||||||
|
// fields.
|
||||||
|
func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
|
||||||
|
lastBlock int32) *MsgVersion {
|
||||||
|
|
||||||
|
// Limit the timestamp to one second precision since the protocol
|
||||||
|
// doesn't support better.
|
||||||
|
return &MsgVersion{
|
||||||
|
ProtocolVersion: int32(ProtocolVersion),
|
||||||
|
Services: 0,
|
||||||
|
Timestamp: time.Unix(time.Now().Unix(), 0),
|
||||||
|
AddrYou: *you,
|
||||||
|
AddrMe: *me,
|
||||||
|
Nonce: nonce,
|
||||||
|
UserAgent: DefaultUserAgent,
|
||||||
|
LastBlock: lastBlock,
|
||||||
|
DisableRelayTx: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateUserAgent checks userAgent length against MaxUserAgentLen
|
||||||
|
func validateUserAgent(userAgent string) error {
|
||||||
|
if len(userAgent) > MaxUserAgentLen {
|
||||||
|
str := fmt.Sprintf("user agent too long [len %v, max %v]",
|
||||||
|
len(userAgent), MaxUserAgentLen)
|
||||||
|
return messageError("MsgVersion", str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUserAgent adds a user agent to the user agent string for the version
|
||||||
|
// message. The version string is not defined to any strict format, although
|
||||||
|
// it is recommended to use the form "major.minor.revision" e.g. "2.6.41".
|
||||||
|
func (msg *MsgVersion) AddUserAgent(name string, version string,
|
||||||
|
comments ...string) error {
|
||||||
|
|
||||||
|
newUserAgent := fmt.Sprintf("%s:%s", name, version)
|
||||||
|
if len(comments) != 0 {
|
||||||
|
newUserAgent = fmt.Sprintf("%s(%s)", newUserAgent,
|
||||||
|
strings.Join(comments, "; "))
|
||||||
|
}
|
||||||
|
newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent)
|
||||||
|
err := validateUserAgent(newUserAgent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.UserAgent = newUserAgent
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
// Copyright (c) 2013-2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// maxNetAddressPayload returns the max payload size for a bitcoin NetAddress
|
||||||
|
// based on the protocol version.
|
||||||
|
func maxNetAddressPayload(pver uint32) uint32 {
|
||||||
|
// Services 8 bytes + ip 16 bytes + port 2 bytes.
|
||||||
|
plen := uint32(26)
|
||||||
|
|
||||||
|
// NetAddressTimeVersion added a timestamp field.
|
||||||
|
if pver >= NetAddressTimeVersion {
|
||||||
|
// Timestamp 4 bytes.
|
||||||
|
plen += 4
|
||||||
|
}
|
||||||
|
|
||||||
|
return plen
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetAddress defines information about a peer on the network including the time
|
||||||
|
// it was last seen, the services it supports, its IP address, and port.
|
||||||
|
type NetAddress struct {
|
||||||
|
// Last time the address was seen. This is, unfortunately, encoded as a
|
||||||
|
// uint32 on the wire and therefore is limited to 2106. This field is
|
||||||
|
// not present in the bitcoin version message (MsgVersion) nor was it
|
||||||
|
// added until protocol version >= NetAddressTimeVersion.
|
||||||
|
Timestamp time.Time
|
||||||
|
|
||||||
|
// Bitfield which identifies the services supported by the address.
|
||||||
|
Services ServiceFlag
|
||||||
|
|
||||||
|
// IP address of the peer.
|
||||||
|
IP net.IP
|
||||||
|
|
||||||
|
// Port the peer is using. This is encoded in big endian on the wire
|
||||||
|
// which differs from most everything else.
|
||||||
|
Port uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasService returns whether the specified service is supported by the address.
|
||||||
|
func (na *NetAddress) HasService(service ServiceFlag) bool {
|
||||||
|
return na.Services&service == service
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddService adds service as a supported service by the peer generating the
|
||||||
|
// message.
|
||||||
|
func (na *NetAddress) AddService(service ServiceFlag) {
|
||||||
|
na.Services |= service
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
|
||||||
|
// supported services with defaults for the remaining fields.
|
||||||
|
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
|
||||||
|
return NewNetAddressTimestamp(time.Now(), services, ip, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetAddressTimestamp returns a new NetAddress using the provided
|
||||||
|
// timestamp, IP, port, and supported services. The timestamp is rounded to
|
||||||
|
// single second precision.
|
||||||
|
func NewNetAddressTimestamp(
|
||||||
|
timestamp time.Time, services ServiceFlag, ip net.IP, port uint16) *NetAddress {
|
||||||
|
// Limit the timestamp to one second precision since the protocol
|
||||||
|
// doesn't support better.
|
||||||
|
na := NetAddress{
|
||||||
|
Timestamp: time.Unix(timestamp.Unix(), 0),
|
||||||
|
Services: services,
|
||||||
|
IP: ip,
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
return &na
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetAddress returns a new NetAddress using the provided TCP address and
|
||||||
|
// supported services with defaults for the remaining fields.
|
||||||
|
func NewNetAddress(addr *net.TCPAddr, services ServiceFlag) *NetAddress {
|
||||||
|
return NewNetAddressIPPort(addr.IP, uint16(addr.Port), services)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readNetAddress reads an encoded NetAddress from r depending on the protocol
|
||||||
|
// version and whether or not the timestamp is included per ts. Some messages
|
||||||
|
// like version do not include the timestamp.
|
||||||
|
func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
|
||||||
|
var ip [16]byte
|
||||||
|
|
||||||
|
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
|
||||||
|
// stop working somewhere around 2106. Also timestamp wasn't added until
|
||||||
|
// protocol version >= NetAddressTimeVersion
|
||||||
|
if ts && pver >= NetAddressTimeVersion {
|
||||||
|
err := readElement(r, (*uint32Time)(&na.Timestamp))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := readElements(r, &na.Services, &ip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Sigh. Bitcoin protocol mixes little and big endian.
|
||||||
|
port, err := binarySerializer.Uint16(r, bigEndian)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*na = NetAddress{
|
||||||
|
Timestamp: na.Timestamp,
|
||||||
|
Services: na.Services,
|
||||||
|
IP: net.IP(ip[:]),
|
||||||
|
Port: port,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeNetAddress serializes a NetAddress to w depending on the protocol
|
||||||
|
// version and whether or not the timestamp is included per ts. Some messages
|
||||||
|
// like version do not include the timestamp.
|
||||||
|
func writeNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error {
|
||||||
|
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
|
||||||
|
// stop working somewhere around 2106. Also timestamp wasn't added until
|
||||||
|
// until protocol version >= NetAddressTimeVersion.
|
||||||
|
if ts && pver >= NetAddressTimeVersion {
|
||||||
|
err := writeElement(w, uint32(na.Timestamp.Unix()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure to always write 16 bytes even if the ip is nil.
|
||||||
|
var ip [16]byte
|
||||||
|
if na.IP != nil {
|
||||||
|
copy(ip[:], na.IP.To16())
|
||||||
|
}
|
||||||
|
err := writeElements(w, na.Services, ip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sigh. Bitcoin protocol mixes little and big endian.
|
||||||
|
return binary.Write(w, bigEndian, na.Port)
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
// 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 wire
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XXX pedro: we will probably need to bump this.
|
||||||
|
const (
|
||||||
|
// ProtocolVersion is the latest protocol version this package supports.
|
||||||
|
ProtocolVersion uint32 = 70013
|
||||||
|
|
||||||
|
// MultipleAddressVersion is the protocol version which added multiple
|
||||||
|
// addresses per message (pver >= MultipleAddressVersion).
|
||||||
|
MultipleAddressVersion uint32 = 209
|
||||||
|
|
||||||
|
// NetAddressTimeVersion is the protocol version which added the
|
||||||
|
// timestamp field (pver >= NetAddressTimeVersion).
|
||||||
|
NetAddressTimeVersion uint32 = 31402
|
||||||
|
|
||||||
|
// BIP0031Version is the protocol version AFTER which a pong message
|
||||||
|
// and nonce field in ping were added (pver > BIP0031Version).
|
||||||
|
BIP0031Version uint32 = 60000
|
||||||
|
|
||||||
|
// BIP0035Version is the protocol version which added the mempool
|
||||||
|
// message (pver >= BIP0035Version).
|
||||||
|
BIP0035Version uint32 = 60002
|
||||||
|
|
||||||
|
// BIP0037Version is the protocol version which added new connection
|
||||||
|
// bloom filtering related messages and extended the version message
|
||||||
|
// with a relay flag (pver >= BIP0037Version).
|
||||||
|
BIP0037Version uint32 = 70001
|
||||||
|
|
||||||
|
// RejectVersion is the protocol version which added a new reject
|
||||||
|
// message.
|
||||||
|
RejectVersion uint32 = 70002
|
||||||
|
|
||||||
|
// BIP0111Version is the protocol version which added the SFNodeBloom
|
||||||
|
// service flag.
|
||||||
|
BIP0111Version uint32 = 70011
|
||||||
|
|
||||||
|
// SendHeadersVersion is the protocol version which added a new
|
||||||
|
// sendheaders message.
|
||||||
|
SendHeadersVersion uint32 = 70012
|
||||||
|
|
||||||
|
// FeeFilterVersion is the protocol version which added a new
|
||||||
|
// feefilter message.
|
||||||
|
FeeFilterVersion uint32 = 70013
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServiceFlag identifies services supported by a bitcoin peer.
|
||||||
|
type ServiceFlag uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SFNodeNetwork is a flag used to indicate a peer is a full node.
|
||||||
|
SFNodeNetwork ServiceFlag = 1 << iota
|
||||||
|
|
||||||
|
// SFNodeGetUTXO is a flag used to indicate a peer supports the
|
||||||
|
// getutxos and utxos commands (BIP0064).
|
||||||
|
SFNodeGetUTXO
|
||||||
|
|
||||||
|
// SFNodeBloom is a flag used to indicate a peer supports bloom
|
||||||
|
// filtering.
|
||||||
|
SFNodeBloom
|
||||||
|
|
||||||
|
// SFNodeWitness is a flag used to indicate a peer supports blocks
|
||||||
|
// and transactions including witness data (BIP0144).
|
||||||
|
SFNodeWitness
|
||||||
|
|
||||||
|
// SFNodeXthin is a flag used to indicate a peer supports xthin blocks.
|
||||||
|
SFNodeXthin
|
||||||
|
|
||||||
|
// SFNodeBit5 is a flag used to indicate a peer supports a service
|
||||||
|
// defined by bit 5.
|
||||||
|
SFNodeBit5
|
||||||
|
|
||||||
|
// SFNodeCF is a flag used to indicate a peer supports committed
|
||||||
|
// filters (CFs).
|
||||||
|
SFNodeCF
|
||||||
|
|
||||||
|
// SFNode2X is a flag used to indicate a peer is running the Segwit2X
|
||||||
|
// software.
|
||||||
|
SFNode2X
|
||||||
|
)
|
||||||
|
|
||||||
|
// Map of service flags back to their constant names for pretty printing.
|
||||||
|
var sfStrings = map[ServiceFlag]string{
|
||||||
|
SFNodeNetwork: "SFNodeNetwork",
|
||||||
|
SFNodeGetUTXO: "SFNodeGetUTXO",
|
||||||
|
SFNodeBloom: "SFNodeBloom",
|
||||||
|
SFNodeWitness: "SFNodeWitness",
|
||||||
|
SFNodeXthin: "SFNodeXthin",
|
||||||
|
SFNodeBit5: "SFNodeBit5",
|
||||||
|
SFNodeCF: "SFNodeCF",
|
||||||
|
SFNode2X: "SFNode2X",
|
||||||
|
}
|
||||||
|
|
||||||
|
// orderedSFStrings is an ordered list of service flags from highest to
|
||||||
|
// lowest.
|
||||||
|
var orderedSFStrings = []ServiceFlag{
|
||||||
|
SFNodeNetwork,
|
||||||
|
SFNodeGetUTXO,
|
||||||
|
SFNodeBloom,
|
||||||
|
SFNodeWitness,
|
||||||
|
SFNodeXthin,
|
||||||
|
SFNodeBit5,
|
||||||
|
SFNodeCF,
|
||||||
|
SFNode2X,
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the ServiceFlag in human-readable form.
|
||||||
|
func (f ServiceFlag) String() string {
|
||||||
|
// No flags are set.
|
||||||
|
if f == 0 {
|
||||||
|
return "0x0"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add individual bit flags.
|
||||||
|
s := ""
|
||||||
|
for _, flag := range orderedSFStrings {
|
||||||
|
if f&flag == flag {
|
||||||
|
s += sfStrings[flag] + "|"
|
||||||
|
f -= flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any remaining flags which aren't accounted for as hex.
|
||||||
|
s = strings.TrimRight(s, "|")
|
||||||
|
if f != 0 {
|
||||||
|
s += "|0x" + strconv.FormatUint(uint64(f), 16)
|
||||||
|
}
|
||||||
|
s = strings.TrimLeft(s, "|")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// BitcoinNet represents which bitcoin network a message belongs to.
|
||||||
|
type BitcoinNet uint32
|
||||||
|
|
||||||
|
// Constants used to indicate the message bitcoin network. They can also be
|
||||||
|
// used to seek to the next message when a stream's state is unknown, but
|
||||||
|
// this package does not provide that functionality since it's generally a
|
||||||
|
// better idea to simply disconnect clients that are misbehaving over TCP.
|
||||||
|
const (
|
||||||
|
// MainNet represents the main bitcoin network.
|
||||||
|
MainNet BitcoinNet = 0xd9b4bef9
|
||||||
|
|
||||||
|
// TestNet represents the regression test network.
|
||||||
|
TestNet BitcoinNet = 0xdab5bffa
|
||||||
|
|
||||||
|
// TestNet3 represents the test network (version 3).
|
||||||
|
TestNet3 BitcoinNet = 0x0709110b
|
||||||
|
|
||||||
|
// SimNet represents the simulation test network.
|
||||||
|
SimNet BitcoinNet = 0x12141c16
|
||||||
|
)
|
||||||
|
|
||||||
|
// bnStrings is a map of bitcoin networks back to their constant names for
|
||||||
|
// pretty printing.
|
||||||
|
var bnStrings = map[BitcoinNet]string{
|
||||||
|
MainNet: "MainNet",
|
||||||
|
TestNet: "TestNet",
|
||||||
|
TestNet3: "TestNet3",
|
||||||
|
SimNet: "SimNet",
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the BitcoinNet in human-readable form.
|
||||||
|
func (n BitcoinNet) String() string {
|
||||||
|
if s, ok := bnStrings[n]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Unknown BitcoinNet (%d)", uint32(n))
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Temp files
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
|
@ -0,0 +1,13 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
sudo: false
|
||||||
|
install:
|
||||||
|
- go get -d -t -v ./...
|
||||||
|
- go get -v golang.org/x/tools/cmd/cover
|
||||||
|
- go get -v github.com/bradfitz/goimports
|
||||||
|
- go get -v github.com/golang/lint/golint
|
||||||
|
script:
|
||||||
|
- export PATH=$PATH:$HOME/gopath/bin
|
||||||
|
- ./goclean.sh
|
|
@ -0,0 +1,15 @@
|
||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2013-2014 Conformal Systems LLC.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue