Release v0.3.0

This commit is contained in:
Manu Herrera 2020-11-09 10:05:29 -03:00
parent 4e9aa7a3c5
commit 8107c4478b
1265 changed files with 440488 additions and 107809 deletions

13
go.mod
View File

@ -3,13 +3,14 @@ module github.com/muun/recovery_tool
go 1.12
require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcd v0.21.0-beta
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/btcsuite/btcwallet v0.10.0
github.com/btcsuite/btcwallet/walletdb v1.1.0
github.com/lightninglabs/neutrino v0.10.0
github.com/muun/libwallet v0.2.0
github.com/btcsuite/btcutil v1.0.2
github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a
github.com/btcsuite/btcwallet/walletdb v1.3.3
github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200
github.com/muun/libwallet v0.5.0
github.com/pkg/errors v0.9.1 // indirect
)
replace github.com/lightninglabs/neutrino => github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257

179
go.sum
View File

@ -1,14 +1,21 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ=
github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
@ -16,49 +23,87 @@ github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcug
github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.20.1-beta.0.20200513120220-b470eee47728/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.20.1-beta.0.20200515232429-9f0179fd2c46/go.mod h1:Yktc19YNjh/Iz2//CX0vfRTS4IJKM/RKO5YZ9Fn+Pgo=
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/btcutil/psbt v1.0.2 h1:gCVY3KxdoEVU7Q6TjusPO+GANIwVgr9yTLqM+a6CZr8=
github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ=
github.com/btcsuite/btcwallet v0.10.0 h1:fFZncfYJ7VByePTGttzJc3qfCyDzU95ucZYk0M912lU=
github.com/btcsuite/btcwallet v0.10.0/go.mod h1:4TqBEuceheGNdeLNrelliLHJzmXauMM2vtWfuy1pFiM=
github.com/btcsuite/btcwallet v0.10.1-0.20191109031858-c49e7ef3ecf1/go.mod h1:4TqBEuceheGNdeLNrelliLHJzmXauMM2vtWfuy1pFiM=
github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a h1:AZ1Mf0gd9mgJqrTTIFUc17ep9EKUbQusVAIzJ6X+x3Q=
github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a/go.mod h1:9+AH3V5mcTtNXTKe+fe63fDLKGOwQbZqmvOVUef+JFE=
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0 h1:KGHMW5sd7yDdDMkCZ/JpP0KltolFsQcB973brBnfj4c=
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA=
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0 h1:6DxkcoMnCPY4E9cUDPB5tbuuf40SmmMkSQkoE8vCT+s=
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
github.com/btcsuite/btcwallet/walletdb v1.0.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk=
github.com/btcsuite/btcwallet/walletdb v1.1.0 h1:JHAL7wZ8pX4SULabeAv/wPO9sseRWMGzE80lfVmRw6Y=
github.com/btcsuite/btcwallet/walletdb v1.1.0/go.mod h1:bZTy9RyYZh9fLnSua+/CD48TJtYJSHjjYcSaszuxCCk=
github.com/btcsuite/btcwallet/walletdb v1.3.1/go.mod h1:9cwc1Yyg4uvd4ZdfdoMnALji+V9gfWSMfxEdLdR5Vwc=
github.com/btcsuite/btcwallet/walletdb v1.3.2/go.mod h1:GZCMPNpUu5KE3ASoVd+k06p/1OW8OwNGCCaNWRto2cQ=
github.com/btcsuite/btcwallet/walletdb v1.3.3 h1:u6e7vRIKBF++cJy+hOHaMGg+88ZTwvpaY27AFvtB668=
github.com/btcsuite/btcwallet/walletdb v1.3.3/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
github.com/btcsuite/btcwallet/wtxmgr v1.0.0 h1:aIHgViEmZmZfe0tQQqF1xyd2qBqFWxX5vZXkkbjtbeA=
github.com/btcsuite/btcwallet/wtxmgr v1.0.0/go.mod h1:vc4gBprll6BP0UJ+AIGDaySoc7MdAmZf8kelfNb8CFY=
github.com/btcsuite/btcwallet/wtxmgr v1.2.0 h1:ZUYPsSv8GjF9KK7lboB2OVHF0uYEcHxgrCfFWqPd9NA=
github.com/btcsuite/btcwallet/wtxmgr v1.2.0/go.mod h1:h8hkcKUE3X7lMPzTUoGnNiw5g7VhGrKEW3KpR2r0VnY=
github.com/btcsuite/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:QcFA8DZHtuIAdYKCq/BzELOaznRsCvwf4zTPmaYwaig=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/golangcrypto v0.0.0-20150304025918-53f62d9b43e8/go.mod h1:tYvUd8KLhm/oXvUeSEs2VlLghFjQt9+ZaF9ghH0JNjc=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/goleveldb v1.0.0 h1:Tvd0BfvqX9o823q1j2UZ/epQo09eJh6dTcRp79ilIN4=
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/snappy-go v1.0.0 h1:ZxaA6lo2EpxGddsA8JwWOcxlzRybb444sgmeJQMJGQE=
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -66,15 +111,42 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v0.0.0-20170724004829-f2862b476edc/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.8.6/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
github.com/jackpal/go-nat-pmp v0.0.0-20170405195558-28a68d0c24ad/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/juju/clock v0.0.0-20190205081909-9c5c9712527c/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
github.com/juju/errors v0.0.0-20190806202954-0232dcc7464d/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
@ -93,22 +165,44 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d h1:tt8hwvxl6fksSfchjBGaWu+pnWJQfG1OWiCM20qOSAE=
github.com/lightninglabs/gozmq v0.0.0-20190710231225-cea2a031735d/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/protobuf-hex-display v1.3.3-0.20191212020323-b444784ce75d/go.mod h1:KDb67YMzoh4eudnzClmvs2FbiLG9vxISmLApUkCa4uI=
github.com/lightningnetwork/lightning-onion v0.0.0-20190909101754-850081b08b6a/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
github.com/lightningnetwork/lightning-onion v1.0.1 h1:qChGgS5+aPxFeR6JiUsGvanei1bn6WJpYbvosw/1604=
github.com/lightningnetwork/lightning-onion v1.0.1/go.mod h1:rigfi6Af/KqsF7Za0hOgcyq2PNH4AN70AaMRxcJkff4=
github.com/lightningnetwork/lnd v0.8.0-beta h1:HmmhSRTq48qobqQF8YLqNa8eKU8dDBNbWWpr2VzycJM=
github.com/lightningnetwork/lnd v0.8.0-beta/go.mod h1:nq06y2BDv7vwWeMmwgB7P3pT7/Uj7sGf5FzHISVD6t4=
github.com/lightningnetwork/lnd v0.10.4-beta h1:Af2zOCPePeaU8Tkl8IqtTjr4BP3zYfi+hAtQYcCMM58=
github.com/lightningnetwork/lnd v0.10.4-beta/go.mod h1:4d02pduRVtZwgTJ+EimKJTsEAY0jDwi0SPE9h5aRneM=
github.com/lightningnetwork/lnd/cert v1.0.2/go.mod h1:fmtemlSMf5t4hsQmcprSoOykypAPp+9c+0d0iqTScMo=
github.com/lightningnetwork/lnd/clock v1.0.1 h1:QQod8+m3KgqHdvVMV+2DRNNZS1GRFir8mHZYA+Z2hFo=
github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg=
github.com/lightningnetwork/lnd/queue v1.0.1 h1:jzJKcTy3Nj5lQrooJ3aaw9Lau3I0IwvQR5sqtjdv2R0=
github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms=
github.com/lightningnetwork/lnd/queue v1.0.4 h1:8Dq3vxAFSACPy+pKN88oPFhuCpCoAAChPBwa4BJxH4k=
github.com/lightningnetwork/lnd/queue v1.0.4/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg=
github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU=
github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw=
github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY=
github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8 h1:PRMAcldsl4mXKJeRNB/KVNz6TlbS6hk2Rs42PqgU3Ws=
github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/muun/libwallet v0.2.0 h1:Y64skcAZnwuxuSmoDPDui9RQv+3fxShDMN3UiROQ7CA=
github.com/muun/libwallet v0.2.0/go.mod h1:eOp7//0x8kWLXWuUyRtQlJ5WN0ZIaQNhL9++LOincrM=
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/muun/libwallet v0.5.0 h1:3YcUuQsnViXdrXntBwV3sLH2RKHC5uNODhuawp+2dg8=
github.com/muun/libwallet v0.5.0/go.mod h1:EdLg8d1sGJ4q4VUKRJyfNDBnbWc+rs5b8pHHu6KF5LY=
github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257 h1:NW17wq2gZlEFeW3/Zx3wSmqlD0wKGf7YvhpP+CNCsbE=
github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -120,45 +214,87 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02/go.mod h1:tHlrkM198S068ZqfrO6S8HsoJq2bF3ETfTL+kt4tInY=
github.com/urfave/cli v1.18.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50 h1:ASw9n1EHMftwnP3Az4XW6e308+gNsrHzmdhd0Olz9Hs=
go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20200720140940-1a48f808d81f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -169,24 +305,63 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
gopkg.in/macaroon-bakery.v2 v2.0.1/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETfr3S9MbIA=
gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -8,24 +8,24 @@ import (
)
func buildExtendedKey(rawKey, recoveryCode string) *libwallet.DecryptedPrivateKey {
recoveryCodeBytes := extractBytes(recoveryCode)
salt := extractSalt(rawKey)
privKey := libwallet.NewChallengePrivateKey(recoveryCodeBytes, salt)
decryptionKey, err := libwallet.RecoveryCodeToKey(recoveryCode, salt)
if err != nil {
log.Fatalf("failed to process recovery code: %v", err)
}
key, err := privKey.DecryptKey(rawKey, libwallet.Mainnet())
walletKey, err := decryptionKey.DecryptKey(rawKey, libwallet.Mainnet())
if err != nil {
log.Fatalf("failed to decrypt key: %v", err)
}
return key
return walletKey
}
func extractSalt(rawKey string) []byte {
func extractSalt(rawKey string) string {
bytes := base58.Decode(rawKey)
return bytes[len(bytes)-8:]
}
saltBytes := bytes[len(bytes)-8:]
func extractBytes(recoveryCode string) []byte {
return []byte(recoveryCode)
return string(saltBytes)
}

241
main.go Normal file
View File

@ -0,0 +1,241 @@
package main
import (
"fmt"
"log"
"os"
"strconv"
"strings"
"github.com/btcsuite/btcutil"
)
func main() {
chainService, close, _ := startChainService()
defer close()
printWelcomeMessage()
recoveryCode := readRecoveryCode()
userRawKey := readKey("first encrypted private key", 147)
userKey := buildExtendedKey(userRawKey, recoveryCode)
userKey.Key.Path = "m/1'/1'"
muunRawKey := readKey("second encrypted private key", 147)
muunKey := buildExtendedKey(muunRawKey, recoveryCode)
sweepAddress := readSweepAddress()
fmt.Println("")
fmt.Println("Preparing to scan the blockchain from your wallet creation block")
fmt.Println("Note that only confirmed transactions can be detected")
fmt.Println("\nThis may take a while")
sweeper := Sweeper{
ChainService: chainService,
UserKey: userKey.Key,
MuunKey: muunKey.Key,
Birthday: muunKey.Birthday,
SweepAddress: sweepAddress,
}
utxos := sweeper.GetUTXOs()
fmt.Println("")
if len(utxos) > 0 {
fmt.Printf("The recovery tool has found the following confirmed UTXOs:\n%v", utxos)
} else {
fmt.Printf("No confirmed UTXOs found")
fmt.Println()
return
}
fmt.Println()
txOutputAmount, txWeightInBytes, err := sweeper.GetSweepTxAmountAndWeightInBytes(utxos)
if err != nil {
printError(err)
}
fee := readFee(txOutputAmount, txWeightInBytes)
// Then we re-build the sweep tx with the actual fee
sweepTx, err := sweeper.BuildSweepTx(utxos, fee)
if err != nil {
printError(err)
}
fmt.Println("Transaction ready to be sent")
err = sweeper.BroadcastTx(sweepTx)
if err != nil {
printError(err)
}
fmt.Printf("Transaction sent! You can check the status here: https://blockstream.info/tx/%v", sweepTx.TxHash().String())
fmt.Println("")
fmt.Printf("We appreciate all kinds of feedback. If you have any, send it to contact@muun.com")
fmt.Println("")
}
func printError(err error) {
log.Printf("The recovery tool failed with the following error: %v", err.Error())
log.Printf("")
log.Printf("You can try again or contact us at support@muun.com")
panic(err)
}
func printWelcomeMessage() {
fmt.Println("Welcome to Muun's Recovery Tool")
fmt.Println("")
fmt.Println("You can use this tool to transfer all funds from your Muun account to an")
fmt.Println("address of your choosing.")
fmt.Println("")
fmt.Println("To do this you will need:")
fmt.Println("1. Your Recovery Code, which you wrote down during your security setup")
fmt.Println("2. Your two encrypted private keys, which you exported from your wallet")
fmt.Println("3. A destination bitcoin address where all your funds will be sent")
fmt.Println("")
fmt.Println("If you have any questions, we'll be happy to answer them. Contact us at support@muun.com")
fmt.Println("")
}
func readRecoveryCode() string {
fmt.Println("")
fmt.Printf("Enter your Recovery Code")
fmt.Println()
fmt.Println("(it looks like this: 'ABCD-1234-POW2-R561-P120-JK26-12RW-45TT')")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
userInput = strings.TrimSpace(userInput)
finalRC := strings.ToUpper(userInput)
if strings.Count(finalRC, "-") != 7 {
fmt.Printf("Invalid recovery code. Did you add the '-' separator between each 4-characters segment?")
fmt.Println()
fmt.Println("Please, try again")
return readRecoveryCode()
}
if len(finalRC) != 39 {
fmt.Println("Your recovery code must have 39 characters")
fmt.Println("Please, try again")
return readRecoveryCode()
}
return finalRC
}
func readKey(keyType string, characters int) string {
fmt.Println("")
fmt.Printf("Enter your %v", keyType)
fmt.Println()
fmt.Println("(it looks like this: '9xzpc7y6sNtRvh8Fh...')")
fmt.Print("> ")
userInput := scanMultiline(characters)
if len(userInput) != characters {
fmt.Printf("Your %v must have %v characters", keyType, characters)
fmt.Println("")
fmt.Println("Please, try again")
return readKey(keyType, characters)
}
return userInput
}
func readSweepAddress() btcutil.Address {
fmt.Println("")
fmt.Println("Enter your destination bitcoin address")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
userInput = strings.TrimSpace(userInput)
addr, err := btcutil.DecodeAddress(userInput, &chainParams)
if err != nil {
fmt.Println("This is not a valid bitcoin address")
fmt.Println("")
fmt.Println("Please, try again")
return readSweepAddress()
}
return addr
}
func readFee(totalBalance, weight int64) int64 {
fmt.Println("")
fmt.Printf("Enter the fee in satoshis per byte. Tx weight: %v bytes. You can check the status of the mempool here: https://bitcoinfees.earn.com/#fees", weight)
fmt.Println()
fmt.Println("(Example: 5)")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
feeInSatsPerByte, err := strconv.ParseInt(userInput, 10, 64)
if err != nil || feeInSatsPerByte <= 0 {
fmt.Printf("The fee must be a number")
fmt.Println("")
fmt.Println("Please, try again")
return readFee(totalBalance, weight)
}
totalFee := feeInSatsPerByte * weight
if totalBalance-totalFee < 546 {
fmt.Printf("The fee is too high. The amount left must be higher than dust")
fmt.Println("")
fmt.Println("Please, try again")
return readFee(totalBalance, weight)
}
return totalFee
}
func readConfirmation(value, fee int64, address string) {
fmt.Println("")
fmt.Printf("About to send %v satoshis with fee: %v satoshis to %v", value, fee, address)
fmt.Println()
fmt.Println("Confirm? (y/n)")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
if userInput == "y" || userInput == "Y" {
return
}
if userInput == "n" || userInput == "N" {
log.Println()
log.Printf("Recovery tool stopped")
log.Println()
log.Printf("You can try again or contact us at support@muun.com")
os.Exit(1)
}
fmt.Println()
fmt.Println("You can only enter 'y' to accept or 'n' to cancel")
readConfirmation(value, fee, address)
}
func scanMultiline(minChars int) string {
var result strings.Builder
for result.Len() < minChars {
var line string
fmt.Scan(&line)
result.WriteString(strings.TrimSpace(line))
}
return result.String()
}

View File

@ -2,12 +2,7 @@ package main
import (
"bytes"
"encoding/hex"
"fmt"
"log"
"os"
"strconv"
"strings"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
@ -16,80 +11,7 @@ import (
"github.com/muun/libwallet"
)
func main() {
chainService, close, _ := startChainService()
defer close()
printWelcomeMessage()
recoveryCode := readRecoveryCode()
userRawKey := readKey("first encrypted private key", 147)
userKey := buildExtendedKey(userRawKey, recoveryCode)
userKey.Key.Path = "m/1'/1'"
muunRawKey := readKey("second encrypted private key", 147)
muunKey := buildExtendedKey(muunRawKey, recoveryCode)
derivedMuunKey, err := muunKey.Key.DeriveTo("m/1'/1'")
if err != nil {
printError(err)
}
sweepAddress := readSweepAddress()
fmt.Println("")
fmt.Println("Starting to scan the blockchain. This may take a while.")
g := NewAddressGenerator(userKey.Key, muunKey.Key)
g.Generate()
birthday := muunKey.Birthday
if birthday == 0xFFFF {
birthday = 0
}
utxos := startRescan(chainService, g.Addresses(), birthday)
fmt.Println("")
if len(utxos) > 0 {
fmt.Printf("The recovery tool has found the following utxos: %v", utxos)
} else {
fmt.Printf("No utxos found")
fmt.Println()
return
}
fmt.Println()
// This is fun:
// First we build a sweep tx with 0 fee with the only purpouse of seeing its signed size
zeroFeehexSweepTx := buildSweepTx(utxos, sweepAddress, 0)
zeroFeeSweepTx, err := buildSignedTx(utxos, zeroFeehexSweepTx, userKey.Key, derivedMuunKey)
if err != nil {
printError(err)
}
weightInBytes := int64(zeroFeeSweepTx.SerializeSize())
fee := readFee(zeroFeeSweepTx.TxOut[0].Value, weightInBytes)
// Then we re-build the sweep tx with the actual fee
hexSweepTx := buildSweepTx(utxos, sweepAddress, fee)
tx, err := buildSignedTx(utxos, hexSweepTx, userKey.Key, derivedMuunKey)
if err != nil {
printError(err)
}
fmt.Println("Transaction ready to be sent")
err = chainService.SendTransaction(tx)
if err != nil {
printError(err)
}
fmt.Printf("Transaction sent! You can check the status here: https://blockstream.info/tx/%v", tx.TxHash().String())
fmt.Println("")
fmt.Printf("If you have any feedback, feel free to share it with us. Our email is contact@muun.com")
fmt.Println("")
}
func buildSweepTx(utxos []*RelevantTx, sweepAddress btcutil.Address, fee int64) string {
func buildSweepTx(utxos []*RelevantTx, sweepAddress btcutil.Address, fee int64) []byte {
tx := wire.NewMsgTx(2)
value := int64(0)
@ -121,32 +43,26 @@ func buildSweepTx(utxos []*RelevantTx, sweepAddress btcutil.Address, fee int64)
readConfirmation(value, fee, sweepAddress.String())
}
return hex.EncodeToString(writer.Bytes())
return writer.Bytes()
}
func buildSignedTx(utxos []*RelevantTx, hexSweepTx string, userKey *libwallet.HDPrivateKey,
func buildSignedTx(utxos []*RelevantTx, sweepTx []byte, userKey *libwallet.HDPrivateKey,
muunKey *libwallet.HDPrivateKey) (*wire.MsgTx, error) {
pstx, err := libwallet.NewPartiallySignedTransaction(hexSweepTx)
inputList := &libwallet.InputList{}
for _, utxo := range utxos {
inputList.Add(&input{
utxo,
[]byte{},
})
}
pstx, err := libwallet.NewPartiallySignedTransaction(inputList, sweepTx)
if err != nil {
printError(err)
}
for index, utxo := range utxos {
input := &input{
utxo,
[]byte{},
}
pstx.AddInput(input)
sig, err := pstx.MuunSignatureForInput(index, userKey.PublicKey(), muunKey)
if err != nil {
panic(err)
}
input.muunSignature = sig
}
signedTx, err := pstx.Sign(userKey, muunKey.PublicKey())
signedTx, err := pstx.FullySign(userKey, muunKey)
if err != nil {
return nil, err
}
@ -156,155 +72,6 @@ func buildSignedTx(utxos []*RelevantTx, hexSweepTx string, userKey *libwallet.HD
return wireTx, nil
}
func printError(err error) {
log.Printf("The recovery tool failed with the following error: %v", err.Error())
log.Printf("")
log.Printf("You can try again or contact us at support@muun.com")
panic(err)
}
func printWelcomeMessage() {
fmt.Println("Welcome to Muun's Recovery Tool")
fmt.Println("")
fmt.Println("You can use this tool to swipe all the balance in your muun account to an")
fmt.Println("address of your choosing.")
fmt.Println("")
fmt.Println("To do this you will need:")
fmt.Println("* The recovery code, that you set up when you created your muun account")
fmt.Println("* The two encrypted private keys that you exported from your muun wallet")
fmt.Println("* A destination bitcoin address where all your funds will be sent")
fmt.Println("")
fmt.Println("If you have any questions, contact us at contact@muun.com")
fmt.Println("")
}
func readRecoveryCode() string {
fmt.Println("")
fmt.Printf("Enter your Recovery Code")
fmt.Println()
fmt.Println("(it looks like this: 'ABCD-1234-POW2-R561-P120-JK26-12RW-45TT')")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
userInput = strings.TrimSpace(userInput)
finalRC := strings.ToUpper(userInput)
if strings.Count(finalRC, "-") != 7 {
fmt.Printf("Wrong recovery code, remember to add the '-' separator between the 4 characters chunks")
fmt.Println()
fmt.Println("Please, try again")
return readRecoveryCode()
}
if len(finalRC) != 39 {
fmt.Println("Your recovery code must have 39 characters")
fmt.Println("Please, try again")
return readRecoveryCode()
}
return finalRC
}
func readKey(keyType string, characters int) string {
fmt.Println("")
fmt.Printf("Enter your %v", keyType)
fmt.Println()
fmt.Println("(it looks like this: '9xzpc7y6sNtRvh8Fh...')")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
userInput = strings.TrimSpace(userInput)
if len(userInput) != characters {
fmt.Printf("Your %v must have %v characters", keyType, characters)
fmt.Println("")
fmt.Println("Please, try again")
return readKey(keyType, characters)
}
return userInput
}
func readSweepAddress() btcutil.Address {
fmt.Println("")
fmt.Println("Enter your destination bitcoin address")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
userInput = strings.TrimSpace(userInput)
addr, err := btcutil.DecodeAddress(userInput, &chainParams)
if err != nil {
fmt.Println("This is not a valid bitcoin address")
fmt.Println("")
fmt.Println("Please, try again")
return readSweepAddress()
}
return addr
}
func readFee(totalBalance, weight int64) int64 {
fmt.Println("")
fmt.Printf("Enter the fee in satoshis per byte. Tx weight: %v bytes. You can check the status of the mempool here: https://bitcoinfees.earn.com/#fees", weight)
fmt.Println()
fmt.Println("(Example: 5)")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
feeInSatsPerByte, err := strconv.ParseInt(userInput, 10, 64)
if err != nil || feeInSatsPerByte <= 0 {
fmt.Printf("The fee must be a number")
fmt.Println("")
fmt.Println("Please, try again")
return readFee(totalBalance, weight)
}
totalFee := feeInSatsPerByte * weight
if totalBalance-totalFee < 546 {
fmt.Printf("The fee is too high. The amount left must be higher than dust")
fmt.Println("")
fmt.Println("Please, try again")
return readFee(totalBalance, weight)
}
return totalFee
}
func readConfirmation(value, fee int64, address string) {
fmt.Println("")
fmt.Printf("About to send %v satoshis with fee: %v satoshis to %v", value, fee, address)
fmt.Println()
fmt.Println("Confirm? (y/n)")
fmt.Print("> ")
var userInput string
fmt.Scan(&userInput)
if userInput == "y" || userInput == "Y" {
return
}
if userInput == "n" || userInput == "N" {
log.Println()
log.Printf("Recovery tool stopped")
log.Println()
log.Printf("You can try again or contact us at support@muun.com")
os.Exit(1)
}
fmt.Println()
fmt.Println("You can only enter 'y' to accept or 'n' to cancel")
readConfirmation(value, fee, address)
}
type input struct {
tx *RelevantTx
muunSignature []byte
@ -334,6 +101,10 @@ func (i *input) SubmarineSwapV2() libwallet.InputSubmarineSwapV2 {
return nil
}
func (i *input) IncomingSwap() libwallet.InputIncomingSwap {
return nil
}
type outpoint struct {
tx *RelevantTx
}

54
sweeper.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/neutrino"
"github.com/muun/libwallet"
)
type Sweeper struct {
ChainService *neutrino.ChainService
UserKey *libwallet.HDPrivateKey
MuunKey *libwallet.HDPrivateKey
Birthday int
SweepAddress btcutil.Address
}
func (s *Sweeper) GetUTXOs() []*RelevantTx {
g := NewAddressGenerator(s.UserKey, s.MuunKey)
g.Generate()
birthday := s.Birthday
if birthday == 0xFFFF {
birthday = 0
}
return startRescan(s.ChainService, g.Addresses(), birthday)
}
func (s *Sweeper) GetSweepTxAmountAndWeightInBytes(utxos []*RelevantTx) (outputAmount int64, weightInBytes int64, err error) {
// we build a sweep tx with 0 fee with the only purpose of checking its signed size
zeroFeeSweepTx, err := s.BuildSweepTx(utxos, 0)
if err != nil {
return 0, 0, err
}
outputAmount = zeroFeeSweepTx.TxOut[0].Value
weightInBytes = int64(zeroFeeSweepTx.SerializeSize())
return outputAmount, weightInBytes, nil
}
func (s *Sweeper) BuildSweepTx(utxos []*RelevantTx, fee int64) (*wire.MsgTx, error) {
derivedMuunKey, err := s.MuunKey.DeriveTo("m/1'/1'")
if err != nil {
return nil, err
}
sweepTx := buildSweepTx(utxos, s.SweepAddress, fee)
return buildSignedTx(utxos, sweepTx, s.UserKey, derivedMuunKey)
}
func (s *Sweeper) BroadcastTx(tx *wire.MsgTx) error {
return s.ChainService.SendTransaction(tx)
}

25
vendor/github.com/aead/chacha20/.gitignore generated vendored Normal file
View File

@ -0,0 +1,25 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
.vscode
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

25
vendor/github.com/aead/chacha20/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,25 @@
language: go
go:
- "1.8.x"
- "1.9.x"
- "1.10.x"
env:
- TRAVIS_GOARCH=amd64
- TRAVIS_GOARCH=386
before_install:
- export GOARCH=$TRAVIS_GOARCH
branches:
only:
- master
before_script:
- go get -u github.com/klauspost/asmfmt/cmd/asmfmt
script:
- diff -au <(gofmt -d .) <(printf "")
- diff -au <(asmfmt -d .) <(printf "")
- go test -v ./...

21
vendor/github.com/aead/chacha20/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Andreas Auernhammer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

82
vendor/github.com/aead/chacha20/README.md generated vendored Normal file
View File

@ -0,0 +1,82 @@
[![Godoc Reference](https://godoc.org/github.com/aead/chacha20?status.svg)](https://godoc.org/github.com/aead/chacha20)
[![Build Status](https://travis-ci.org/aead/chacha20.svg?branch=master)](https://travis-ci.org/aead/chacha20)
[![Go Report Card](https://goreportcard.com/badge/aead/chacha20)](https://goreportcard.com/report/aead/chacha20)
## The ChaCha20 stream cipher
ChaCha is a stream cipher family created by Daniel J. Bernstein.
The most common ChaCha variant is ChaCha20 (20 rounds). ChaCha20 is
standardized in [RFC 7539](https://tools.ietf.org/html/rfc7539 "RFC 7539").
This package provides implementations of three ChaCha versions:
- ChaCha20 with a 64 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
- ChaCha20 with a 96 bit nonce (can en/decrypt up to 2^32 * 64 bytes ~ 256 GB for one key-nonce combination)
- XChaCha20 with a 192 bit nonce (can en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
Furthermore the chacha sub package implements ChaCha20/12 and ChaCha20/8.
These versions use 12 or 8 rounds instead of 20.
But it's recommended to use ChaCha20 (with 20 rounds) - it will be fast enough for almost all purposes.
### Installation
Install in your GOPATH: `go get -u github.com/aead/chacha20`
### Requirements
All go versions >= 1.8.7 are supported.
The code may also work on Go 1.7 but this is not tested.
### Performance
#### AMD64
Hardware: Intel i7-6500U 2.50GHz x 2
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
Go version: 1.8.0
```
AVX2
name speed cpb
ChaCha20_64-4 573MB/s ± 0% 4.16
ChaCha20_1K-4 2.19GB/s ± 0% 1.06
XChaCha20_64-4 261MB/s ± 0% 9.13
XChaCha20_1K-4 1.69GB/s ± 4% 1.37
XORKeyStream64-4 474MB/s ± 2% 5.02
XORKeyStream1K-4 2.09GB/s ± 1% 1.11
XChaCha20_XORKeyStream64-4 262MB/s ± 0% 9.09
XChaCha20_XORKeyStream1K-4 1.71GB/s ± 1% 1.36
SSSE3
name speed cpb
ChaCha20_64-4 583MB/s ± 0% 4.08
ChaCha20_1K-4 1.15GB/s ± 1% 2.02
XChaCha20_64-4 267MB/s ± 0% 8.92
XChaCha20_1K-4 984MB/s ± 5% 2.42
XORKeyStream64-4 492MB/s ± 1% 4.84
XORKeyStream1K-4 1.10GB/s ± 5% 2.11
XChaCha20_XORKeyStream64-4 266MB/s ± 0% 8.96
XChaCha20_XORKeyStream1K-4 1.00GB/s ± 2% 2.32
```
#### 386
Hardware: Intel i7-6500U 2.50GHz x 2
System: Linux Ubuntu 16.04 - kernel: 4.4.0-62-generic
Go version: 1.8.0
```
SSSE3
name                        speed cpb
ChaCha20_64-4               570MB/s ± 0% 4.18
ChaCha20_1K-4               650MB/s ± 0% 3.66
XChaCha20_64-4              223MB/s ± 0% 10.69
XChaCha20_1K-4              584MB/s ± 1% 4.08
XORKeyStream64-4            392MB/s ± 1% 6.08
XORKeyStream1K-4            629MB/s ± 1% 3.79
XChaCha20_XORKeyStream64-4  222MB/s ± 0% 10.73
XChaCha20_XORKeyStream1K-4  585MB/s ± 0% 4.07
SSE2
name speed cpb
ChaCha20_64-4 509MB/s ± 0% 4.68
ChaCha20_1K-4 553MB/s ± 2% 4.31
XChaCha20_64-4 201MB/s ± 0% 11.86
XChaCha20_1K-4 498MB/s ± 4% 4.78
XORKeyStream64-4 359MB/s ± 1% 6.64
XORKeyStream1K-4 545MB/s ± 0% 4.37
XChaCha20_XORKeyStream64-4 201MB/s ± 1% 11.86
XChaCha20_XORKeyStream1K-4 507MB/s ± 0% 4.70
```

197
vendor/github.com/aead/chacha20/chacha/chacha.go generated vendored Normal file
View File

@ -0,0 +1,197 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Package chacha implements some low-level functions of the
// ChaCha cipher family.
package chacha // import "github.com/aead/chacha20/chacha"
import (
"encoding/binary"
"errors"
"math"
)
const (
// NonceSize is the size of the ChaCha20 nonce in bytes.
NonceSize = 8
// INonceSize is the size of the IETF-ChaCha20 nonce in bytes.
INonceSize = 12
// XNonceSize is the size of the XChaCha20 nonce in bytes.
XNonceSize = 24
// KeySize is the size of the key in bytes.
KeySize = 32
)
var (
useSSE2 bool
useSSSE3 bool
useAVX bool
useAVX2 bool
)
var (
errKeySize = errors.New("chacha20/chacha: bad key length")
errInvalidNonce = errors.New("chacha20/chacha: bad nonce length")
)
func setup(state *[64]byte, nonce, key []byte) (err error) {
if len(key) != KeySize {
err = errKeySize
return
}
var Nonce [16]byte
switch len(nonce) {
case NonceSize:
copy(Nonce[8:], nonce)
initialize(state, key, &Nonce)
case INonceSize:
copy(Nonce[4:], nonce)
initialize(state, key, &Nonce)
case XNonceSize:
var tmpKey [32]byte
var hNonce [16]byte
copy(hNonce[:], nonce[:16])
copy(tmpKey[:], key)
HChaCha20(&tmpKey, &hNonce, &tmpKey)
copy(Nonce[8:], nonce[16:])
initialize(state, tmpKey[:], &Nonce)
// BUG(aead): A "good" compiler will remove this (optimizations)
// But using the provided key instead of tmpKey,
// will change the key (-> probably confuses users)
for i := range tmpKey {
tmpKey[i] = 0
}
default:
err = errInvalidNonce
}
return
}
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
// The length of the nonce determinds the version of ChaCha20:
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
// The rounds argument specifies the number of rounds performed for keystream
// generation - valid values are 8, 12 or 20. The src and dst may be the same slice
// but otherwise should not overlap. If len(dst) < len(src) this function panics.
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
func XORKeyStream(dst, src, nonce, key []byte, rounds int) {
if rounds != 20 && rounds != 12 && rounds != 8 {
panic("chacha20/chacha: bad number of rounds")
}
if len(dst) < len(src) {
panic("chacha20/chacha: dst buffer is to small")
}
if len(nonce) == INonceSize && uint64(len(src)) > (1<<38) {
panic("chacha20/chacha: src is too large")
}
var block, state [64]byte
if err := setup(&state, nonce, key); err != nil {
panic(err)
}
xorKeyStream(dst, src, &block, &state, rounds)
}
// Cipher implements ChaCha20/r (XChaCha20/r) for a given number of rounds r.
type Cipher struct {
state, block [64]byte
off int
rounds int // 20 for ChaCha20
noncesize int
}
// NewCipher returns a new *chacha.Cipher implementing the ChaCha20/r or XChaCha20/r
// (r = 8, 12 or 20) stream cipher. The nonce must be unique for one key for all time.
// The length of the nonce determinds the version of ChaCha20:
// - NonceSize: ChaCha20/r with a 64 bit nonce and a 2^64 * 64 byte period.
// - INonceSize: ChaCha20/r as defined in RFC 7539 and a 2^32 * 64 byte period.
// - XNonceSize: XChaCha20/r with a 192 bit nonce and a 2^64 * 64 byte period.
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
func NewCipher(nonce, key []byte, rounds int) (*Cipher, error) {
if rounds != 20 && rounds != 12 && rounds != 8 {
panic("chacha20/chacha: bad number of rounds")
}
c := new(Cipher)
if err := setup(&(c.state), nonce, key); err != nil {
return nil, err
}
c.rounds = rounds
if len(nonce) == INonceSize {
c.noncesize = INonceSize
} else {
c.noncesize = NonceSize
}
return c, nil
}
// XORKeyStream crypts bytes from src to dst. Src and dst may be the same slice
// but otherwise should not overlap. If len(dst) < len(src) the function panics.
func (c *Cipher) XORKeyStream(dst, src []byte) {
if len(dst) < len(src) {
panic("chacha20/chacha: dst buffer is to small")
}
if c.off > 0 {
n := len(c.block[c.off:])
if len(src) <= n {
for i, v := range src {
dst[i] = v ^ c.block[c.off]
c.off++
}
if c.off == 64 {
c.off = 0
}
return
}
for i, v := range c.block[c.off:] {
dst[i] = src[i] ^ v
}
src = src[n:]
dst = dst[n:]
c.off = 0
}
// check for counter overflow
blocksToXOR := len(src) / 64
if len(src)%64 != 0 {
blocksToXOR++
}
var overflow bool
if c.noncesize == INonceSize {
overflow = binary.LittleEndian.Uint32(c.state[48:]) > math.MaxUint32-uint32(blocksToXOR)
} else {
overflow = binary.LittleEndian.Uint64(c.state[48:]) > math.MaxUint64-uint64(blocksToXOR)
}
if overflow {
panic("chacha20/chacha: counter overflow")
}
c.off += xorKeyStream(dst, src, &(c.block), &(c.state), c.rounds)
}
// SetCounter skips ctr * 64 byte blocks. SetCounter(0) resets the cipher.
// This function always skips the unused keystream of the current 64 byte block.
func (c *Cipher) SetCounter(ctr uint64) {
if c.noncesize == INonceSize {
binary.LittleEndian.PutUint32(c.state[48:], uint32(ctr))
} else {
binary.LittleEndian.PutUint64(c.state[48:], ctr)
}
c.off = 0
}
// HChaCha20 generates 32 pseudo-random bytes from a 128 bit nonce and a 256 bit secret key.
// It can be used as a key-derivation-function (KDF).
func HChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) { hChaCha20(out, nonce, key) }

View File

@ -0,0 +1,406 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build amd64,!gccgo,!appengine,!nacl
#include "const.s"
#include "macro.s"
#define TWO 0(SP)
#define C16 32(SP)
#define C8 64(SP)
#define STATE_0 96(SP)
#define STATE_1 128(SP)
#define STATE_2 160(SP)
#define STATE_3 192(SP)
#define TMP_0 224(SP)
#define TMP_1 256(SP)
// func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int
TEXT ·xorKeyStreamAVX2(SB), 4, $320-80
MOVQ dst_base+0(FP), DI
MOVQ src_base+24(FP), SI
MOVQ block+48(FP), BX
MOVQ state+56(FP), AX
MOVQ rounds+64(FP), DX
MOVQ src_len+32(FP), CX
MOVQ SP, R8
ADDQ $32, SP
ANDQ $-32, SP
VMOVDQU 0(AX), Y2
VMOVDQU 32(AX), Y3
VPERM2I128 $0x22, Y2, Y0, Y0
VPERM2I128 $0x33, Y2, Y1, Y1
VPERM2I128 $0x22, Y3, Y2, Y2
VPERM2I128 $0x33, Y3, Y3, Y3
TESTQ CX, CX
JZ done
VMOVDQU ·one_AVX2<>(SB), Y4
VPADDD Y4, Y3, Y3
VMOVDQA Y0, STATE_0
VMOVDQA Y1, STATE_1
VMOVDQA Y2, STATE_2
VMOVDQA Y3, STATE_3
VMOVDQU ·rol16_AVX2<>(SB), Y4
VMOVDQU ·rol8_AVX2<>(SB), Y5
VMOVDQU ·two_AVX2<>(SB), Y6
VMOVDQA Y4, Y14
VMOVDQA Y5, Y15
VMOVDQA Y4, C16
VMOVDQA Y5, C8
VMOVDQA Y6, TWO
CMPQ CX, $64
JBE between_0_and_64
CMPQ CX, $192
JBE between_64_and_192
CMPQ CX, $320
JBE between_192_and_320
CMPQ CX, $448
JBE between_320_and_448
at_least_512:
VMOVDQA Y0, Y4
VMOVDQA Y1, Y5
VMOVDQA Y2, Y6
VPADDQ TWO, Y3, Y7
VMOVDQA Y0, Y8
VMOVDQA Y1, Y9
VMOVDQA Y2, Y10
VPADDQ TWO, Y7, Y11
VMOVDQA Y0, Y12
VMOVDQA Y1, Y13
VMOVDQA Y2, Y14
VPADDQ TWO, Y11, Y15
MOVQ DX, R9
chacha_loop_512:
VMOVDQA Y8, TMP_0
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8)
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8)
VMOVDQA TMP_0, Y8
VMOVDQA Y0, TMP_0
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8)
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8)
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3)
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
CHACHA_SHUFFLE_AVX(Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y12, Y13, Y14, Y15, Y0, C16, C8)
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y0, C16, C8)
VMOVDQA TMP_0, Y0
VMOVDQA Y8, TMP_0
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y8, C16, C8)
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y8, C16, C8)
VMOVDQA TMP_0, Y8
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1)
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
CHACHA_SHUFFLE_AVX(Y15, Y14, Y13)
SUBQ $2, R9
JA chacha_loop_512
VMOVDQA Y12, TMP_0
VMOVDQA Y13, TMP_1
VPADDD STATE_0, Y0, Y0
VPADDD STATE_1, Y1, Y1
VPADDD STATE_2, Y2, Y2
VPADDD STATE_3, Y3, Y3
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
VMOVDQA STATE_0, Y0
VMOVDQA STATE_1, Y1
VMOVDQA STATE_2, Y2
VMOVDQA STATE_3, Y3
VPADDQ TWO, Y3, Y3
VPADDD Y0, Y4, Y4
VPADDD Y1, Y5, Y5
VPADDD Y2, Y6, Y6
VPADDD Y3, Y7, Y7
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
VPADDQ TWO, Y3, Y3
VPADDD Y0, Y8, Y8
VPADDD Y1, Y9, Y9
VPADDD Y2, Y10, Y10
VPADDD Y3, Y11, Y11
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
VPADDQ TWO, Y3, Y3
VPADDD TMP_0, Y0, Y12
VPADDD TMP_1, Y1, Y13
VPADDD Y2, Y14, Y14
VPADDD Y3, Y15, Y15
VPADDQ TWO, Y3, Y3
CMPQ CX, $512
JB less_than_512
XOR_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
VMOVDQA Y3, STATE_3
ADDQ $512, SI
ADDQ $512, DI
SUBQ $512, CX
CMPQ CX, $448
JA at_least_512
TESTQ CX, CX
JZ done
VMOVDQA C16, Y14
VMOVDQA C8, Y15
CMPQ CX, $64
JBE between_0_and_64
CMPQ CX, $192
JBE between_64_and_192
CMPQ CX, $320
JBE between_192_and_320
JMP between_320_and_448
less_than_512:
XOR_UPPER_AVX2(DI, SI, 384, Y12, Y13, Y14, Y15, Y4, Y5)
EXTRACT_LOWER(BX, Y12, Y13, Y14, Y15, Y4)
ADDQ $448, SI
ADDQ $448, DI
SUBQ $448, CX
JMP finalize
between_320_and_448:
VMOVDQA Y0, Y4
VMOVDQA Y1, Y5
VMOVDQA Y2, Y6
VPADDQ TWO, Y3, Y7
VMOVDQA Y0, Y8
VMOVDQA Y1, Y9
VMOVDQA Y2, Y10
VPADDQ TWO, Y7, Y11
MOVQ DX, R9
chacha_loop_384:
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y1, Y2, Y3)
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
CHACHA_QROUND_AVX(Y0, Y1, Y2, Y3, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y3, Y2, Y1)
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
SUBQ $2, R9
JA chacha_loop_384
VPADDD STATE_0, Y0, Y0
VPADDD STATE_1, Y1, Y1
VPADDD STATE_2, Y2, Y2
VPADDD STATE_3, Y3, Y3
XOR_AVX2(DI, SI, 0, Y0, Y1, Y2, Y3, Y12, Y13)
VMOVDQA STATE_0, Y0
VMOVDQA STATE_1, Y1
VMOVDQA STATE_2, Y2
VMOVDQA STATE_3, Y3
VPADDQ TWO, Y3, Y3
VPADDD Y0, Y4, Y4
VPADDD Y1, Y5, Y5
VPADDD Y2, Y6, Y6
VPADDD Y3, Y7, Y7
XOR_AVX2(DI, SI, 128, Y4, Y5, Y6, Y7, Y12, Y13)
VPADDQ TWO, Y3, Y3
VPADDD Y0, Y8, Y8
VPADDD Y1, Y9, Y9
VPADDD Y2, Y10, Y10
VPADDD Y3, Y11, Y11
VPADDQ TWO, Y3, Y3
CMPQ CX, $384
JB less_than_384
XOR_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
SUBQ $384, CX
TESTQ CX, CX
JE done
ADDQ $384, SI
ADDQ $384, DI
JMP between_0_and_64
less_than_384:
XOR_UPPER_AVX2(DI, SI, 256, Y8, Y9, Y10, Y11, Y12, Y13)
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
ADDQ $320, SI
ADDQ $320, DI
SUBQ $320, CX
JMP finalize
between_192_and_320:
VMOVDQA Y0, Y4
VMOVDQA Y1, Y5
VMOVDQA Y2, Y6
VMOVDQA Y3, Y7
VMOVDQA Y0, Y8
VMOVDQA Y1, Y9
VMOVDQA Y2, Y10
VPADDQ TWO, Y3, Y11
MOVQ DX, R9
chacha_loop_256:
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
CHACHA_SHUFFLE_AVX(Y9, Y10, Y11)
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_QROUND_AVX(Y8, Y9, Y10, Y11, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
CHACHA_SHUFFLE_AVX(Y11, Y10, Y9)
SUBQ $2, R9
JA chacha_loop_256
VPADDD Y0, Y4, Y4
VPADDD Y1, Y5, Y5
VPADDD Y2, Y6, Y6
VPADDD Y3, Y7, Y7
VPADDQ TWO, Y3, Y3
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
VPADDD Y0, Y8, Y8
VPADDD Y1, Y9, Y9
VPADDD Y2, Y10, Y10
VPADDD Y3, Y11, Y11
VPADDQ TWO, Y3, Y3
CMPQ CX, $256
JB less_than_256
XOR_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
SUBQ $256, CX
TESTQ CX, CX
JE done
ADDQ $256, SI
ADDQ $256, DI
JMP between_0_and_64
less_than_256:
XOR_UPPER_AVX2(DI, SI, 128, Y8, Y9, Y10, Y11, Y12, Y13)
EXTRACT_LOWER(BX, Y8, Y9, Y10, Y11, Y12)
ADDQ $192, SI
ADDQ $192, DI
SUBQ $192, CX
JMP finalize
between_64_and_192:
VMOVDQA Y0, Y4
VMOVDQA Y1, Y5
VMOVDQA Y2, Y6
VMOVDQA Y3, Y7
MOVQ DX, R9
chacha_loop_128:
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y5, Y6, Y7)
CHACHA_QROUND_AVX(Y4, Y5, Y6, Y7, Y13, Y14, Y15)
CHACHA_SHUFFLE_AVX(Y7, Y6, Y5)
SUBQ $2, R9
JA chacha_loop_128
VPADDD Y0, Y4, Y4
VPADDD Y1, Y5, Y5
VPADDD Y2, Y6, Y6
VPADDD Y3, Y7, Y7
VPADDQ TWO, Y3, Y3
CMPQ CX, $128
JB less_than_128
XOR_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
SUBQ $128, CX
TESTQ CX, CX
JE done
ADDQ $128, SI
ADDQ $128, DI
JMP between_0_and_64
less_than_128:
XOR_UPPER_AVX2(DI, SI, 0, Y4, Y5, Y6, Y7, Y12, Y13)
EXTRACT_LOWER(BX, Y4, Y5, Y6, Y7, Y13)
ADDQ $64, SI
ADDQ $64, DI
SUBQ $64, CX
JMP finalize
between_0_and_64:
VMOVDQA X0, X4
VMOVDQA X1, X5
VMOVDQA X2, X6
VMOVDQA X3, X7
MOVQ DX, R9
chacha_loop_64:
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15)
CHACHA_SHUFFLE_AVX(X5, X6, X7)
CHACHA_QROUND_AVX(X4, X5, X6, X7, X13, X14, X15)
CHACHA_SHUFFLE_AVX(X7, X6, X5)
SUBQ $2, R9
JA chacha_loop_64
VPADDD X0, X4, X4
VPADDD X1, X5, X5
VPADDD X2, X6, X6
VPADDD X3, X7, X7
VMOVDQU ·one<>(SB), X0
VPADDQ X0, X3, X3
CMPQ CX, $64
JB less_than_64
XOR_AVX(DI, SI, 0, X4, X5, X6, X7, X13)
SUBQ $64, CX
JMP done
less_than_64:
VMOVDQU X4, 0(BX)
VMOVDQU X5, 16(BX)
VMOVDQU X6, 32(BX)
VMOVDQU X7, 48(BX)
finalize:
XORQ R11, R11
XORQ R12, R12
MOVQ CX, BP
xor_loop:
MOVB 0(SI), R11
MOVB 0(BX), R12
XORQ R11, R12
MOVB R12, 0(DI)
INCQ SI
INCQ BX
INCQ DI
DECQ BP
JA xor_loop
done:
VMOVDQU X3, 48(AX)
VZEROUPPER
MOVQ R8, SP
MOVQ CX, ret+72(FP)
RET

60
vendor/github.com/aead/chacha20/chacha/chacha_386.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl
package chacha
import (
"encoding/binary"
"golang.org/x/sys/cpu"
)
func init() {
useSSE2 = cpu.X86.HasSSE2
useSSSE3 = cpu.X86.HasSSSE3
useAVX = false
useAVX2 = false
}
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
binary.LittleEndian.PutUint32(state[0:], sigma[0])
binary.LittleEndian.PutUint32(state[4:], sigma[1])
binary.LittleEndian.PutUint32(state[8:], sigma[2])
binary.LittleEndian.PutUint32(state[12:], sigma[3])
copy(state[16:], key[:])
copy(state[48:], nonce[:])
}
// This function is implemented in chacha_386.s
//go:noescape
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_386.s
//go:noescape
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_386.s
//go:noescape
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
switch {
case useSSSE3:
hChaCha20SSSE3(out, nonce, key)
case useSSE2:
hChaCha20SSE2(out, nonce, key)
default:
hChaCha20Generic(out, nonce, key)
}
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
if useSSE2 {
return xorKeyStreamSSE2(dst, src, block, state, rounds)
} else {
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
}

163
vendor/github.com/aead/chacha20/chacha/chacha_386.s generated vendored Normal file
View File

@ -0,0 +1,163 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl
#include "const.s"
#include "macro.s"
// FINALIZE xors len bytes from src and block using
// the temp. registers t0 and t1 and writes the result
// to dst.
#define FINALIZE(dst, src, block, len, t0, t1) \
XORL t0, t0; \
XORL t1, t1; \
FINALIZE_LOOP:; \
MOVB 0(src), t0; \
MOVB 0(block), t1; \
XORL t0, t1; \
MOVB t1, 0(dst); \
INCL src; \
INCL block; \
INCL dst; \
DECL len; \
JG FINALIZE_LOOP \
#define Dst DI
#define Nonce AX
#define Key BX
#define Rounds DX
// func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
TEXT ·hChaCha20SSE2(SB), 4, $0-12
MOVL out+0(FP), Dst
MOVL nonce+4(FP), Nonce
MOVL key+8(FP), Key
MOVOU ·sigma<>(SB), X0
MOVOU 0*16(Key), X1
MOVOU 1*16(Key), X2
MOVOU 0*16(Nonce), X3
MOVL $20, Rounds
chacha_loop:
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
CHACHA_SHUFFLE_SSE(X1, X2, X3)
CHACHA_QROUND_SSE2(X0, X1, X2, X3, X4)
CHACHA_SHUFFLE_SSE(X3, X2, X1)
SUBL $2, Rounds
JNZ chacha_loop
MOVOU X0, 0*16(Dst)
MOVOU X3, 1*16(Dst)
RET
// func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
TEXT ·hChaCha20SSSE3(SB), 4, $0-12
MOVL out+0(FP), Dst
MOVL nonce+4(FP), Nonce
MOVL key+8(FP), Key
MOVOU ·sigma<>(SB), X0
MOVOU 0*16(Key), X1
MOVOU 1*16(Key), X2
MOVOU 0*16(Nonce), X3
MOVL $20, Rounds
MOVOU ·rol16<>(SB), X5
MOVOU ·rol8<>(SB), X6
chacha_loop:
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
CHACHA_SHUFFLE_SSE(X1, X2, X3)
CHACHA_QROUND_SSSE3(X0, X1, X2, X3, X4, X5, X6)
CHACHA_SHUFFLE_SSE(X3, X2, X1)
SUBL $2, Rounds
JNZ chacha_loop
MOVOU X0, 0*16(Dst)
MOVOU X3, 1*16(Dst)
RET
#undef Dst
#undef Nonce
#undef Key
#undef Rounds
#define State AX
#define Dst DI
#define Src SI
#define Len DX
#define Tmp0 BX
#define Tmp1 BP
// func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
TEXT ·xorKeyStreamSSE2(SB), 4, $0-40
MOVL dst_base+0(FP), Dst
MOVL src_base+12(FP), Src
MOVL state+28(FP), State
MOVL src_len+16(FP), Len
MOVL $0, ret+36(FP) // Number of bytes written to the keystream buffer - 0 iff len mod 64 == 0
MOVOU 0*16(State), X0
MOVOU 1*16(State), X1
MOVOU 2*16(State), X2
MOVOU 3*16(State), X3
TESTL Len, Len
JZ DONE
GENERATE_KEYSTREAM:
MOVO X0, X4
MOVO X1, X5
MOVO X2, X6
MOVO X3, X7
MOVL rounds+32(FP), Tmp0
CHACHA_LOOP:
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
CHACHA_SHUFFLE_SSE(X5, X6, X7)
CHACHA_QROUND_SSE2(X4, X5, X6, X7, X0)
CHACHA_SHUFFLE_SSE(X7, X6, X5)
SUBL $2, Tmp0
JA CHACHA_LOOP
MOVOU 0*16(State), X0 // Restore X0 from state
PADDL X0, X4
PADDL X1, X5
PADDL X2, X6
PADDL X3, X7
MOVOU ·one<>(SB), X0
PADDQ X0, X3
CMPL Len, $64
JL BUFFER_KEYSTREAM
XOR_SSE(Dst, Src, 0, X4, X5, X6, X7, X0)
MOVOU 0*16(State), X0 // Restore X0 from state
ADDL $64, Src
ADDL $64, Dst
SUBL $64, Len
JZ DONE
JMP GENERATE_KEYSTREAM // There is at least one more plaintext byte
BUFFER_KEYSTREAM:
MOVL block+24(FP), State
MOVOU X4, 0(State)
MOVOU X5, 16(State)
MOVOU X6, 32(State)
MOVOU X7, 48(State)
MOVL Len, ret+36(FP) // Number of bytes written to the keystream buffer - 0 < Len < 64
FINALIZE(Dst, Src, State, Len, Tmp0, Tmp1)
DONE:
MOVL state+28(FP), State
MOVOU X3, 3*16(State)
RET
#undef State
#undef Dst
#undef Src
#undef Len
#undef Tmp0
#undef Tmp1

76
vendor/github.com/aead/chacha20/chacha/chacha_amd64.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
// Copyright (c) 2017 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build go1.7,amd64,!gccgo,!appengine,!nacl
package chacha
import "golang.org/x/sys/cpu"
func init() {
useSSE2 = cpu.X86.HasSSE2
useSSSE3 = cpu.X86.HasSSSE3
useAVX = cpu.X86.HasAVX
useAVX2 = cpu.X86.HasAVX2
}
// This function is implemented in chacha_amd64.s
//go:noescape
func initialize(state *[64]byte, key []byte, nonce *[16]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSE2(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func hChaCha20SSSE3(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chachaAVX2_amd64.s
//go:noescape
func hChaCha20AVX(out *[32]byte, nonce *[16]byte, key *[32]byte)
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSE2(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamSSSE3(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chacha_amd64.s
//go:noescape
func xorKeyStreamAVX(dst, src []byte, block, state *[64]byte, rounds int) int
// This function is implemented in chachaAVX2_amd64.s
//go:noescape
func xorKeyStreamAVX2(dst, src []byte, block, state *[64]byte, rounds int) int
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
switch {
case useAVX:
hChaCha20AVX(out, nonce, key)
case useSSSE3:
hChaCha20SSSE3(out, nonce, key)
case useSSE2:
hChaCha20SSE2(out, nonce, key)
default:
hChaCha20Generic(out, nonce, key)
}
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
switch {
case useAVX2:
return xorKeyStreamAVX2(dst, src, block, state, rounds)
case useAVX:
return xorKeyStreamAVX(dst, src, block, state, rounds)
case useSSSE3:
return xorKeyStreamSSSE3(dst, src, block, state, rounds)
case useSSE2:
return xorKeyStreamSSE2(dst, src, block, state, rounds)
default:
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
}

1072
vendor/github.com/aead/chacha20/chacha/chacha_amd64.s generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,319 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
package chacha
import "encoding/binary"
var sigma = [4]uint32{0x61707865, 0x3320646e, 0x79622d32, 0x6b206574}
func xorKeyStreamGeneric(dst, src []byte, block, state *[64]byte, rounds int) int {
for len(src) >= 64 {
chachaGeneric(block, state, rounds)
for i, v := range block {
dst[i] = src[i] ^ v
}
src = src[64:]
dst = dst[64:]
}
n := len(src)
if n > 0 {
chachaGeneric(block, state, rounds)
for i, v := range src {
dst[i] = v ^ block[i]
}
}
return n
}
func chachaGeneric(dst *[64]byte, state *[64]byte, rounds int) {
v00 := binary.LittleEndian.Uint32(state[0:])
v01 := binary.LittleEndian.Uint32(state[4:])
v02 := binary.LittleEndian.Uint32(state[8:])
v03 := binary.LittleEndian.Uint32(state[12:])
v04 := binary.LittleEndian.Uint32(state[16:])
v05 := binary.LittleEndian.Uint32(state[20:])
v06 := binary.LittleEndian.Uint32(state[24:])
v07 := binary.LittleEndian.Uint32(state[28:])
v08 := binary.LittleEndian.Uint32(state[32:])
v09 := binary.LittleEndian.Uint32(state[36:])
v10 := binary.LittleEndian.Uint32(state[40:])
v11 := binary.LittleEndian.Uint32(state[44:])
v12 := binary.LittleEndian.Uint32(state[48:])
v13 := binary.LittleEndian.Uint32(state[52:])
v14 := binary.LittleEndian.Uint32(state[56:])
v15 := binary.LittleEndian.Uint32(state[60:])
s00, s01, s02, s03, s04, s05, s06, s07 := v00, v01, v02, v03, v04, v05, v06, v07
s08, s09, s10, s11, s12, s13, s14, s15 := v08, v09, v10, v11, v12, v13, v14, v15
for i := 0; i < rounds; i += 2 {
v00 += v04
v12 ^= v00
v12 = (v12 << 16) | (v12 >> 16)
v08 += v12
v04 ^= v08
v04 = (v04 << 12) | (v04 >> 20)
v00 += v04
v12 ^= v00
v12 = (v12 << 8) | (v12 >> 24)
v08 += v12
v04 ^= v08
v04 = (v04 << 7) | (v04 >> 25)
v01 += v05
v13 ^= v01
v13 = (v13 << 16) | (v13 >> 16)
v09 += v13
v05 ^= v09
v05 = (v05 << 12) | (v05 >> 20)
v01 += v05
v13 ^= v01
v13 = (v13 << 8) | (v13 >> 24)
v09 += v13
v05 ^= v09
v05 = (v05 << 7) | (v05 >> 25)
v02 += v06
v14 ^= v02
v14 = (v14 << 16) | (v14 >> 16)
v10 += v14
v06 ^= v10
v06 = (v06 << 12) | (v06 >> 20)
v02 += v06
v14 ^= v02
v14 = (v14 << 8) | (v14 >> 24)
v10 += v14
v06 ^= v10
v06 = (v06 << 7) | (v06 >> 25)
v03 += v07
v15 ^= v03
v15 = (v15 << 16) | (v15 >> 16)
v11 += v15
v07 ^= v11
v07 = (v07 << 12) | (v07 >> 20)
v03 += v07
v15 ^= v03
v15 = (v15 << 8) | (v15 >> 24)
v11 += v15
v07 ^= v11
v07 = (v07 << 7) | (v07 >> 25)
v00 += v05
v15 ^= v00
v15 = (v15 << 16) | (v15 >> 16)
v10 += v15
v05 ^= v10
v05 = (v05 << 12) | (v05 >> 20)
v00 += v05
v15 ^= v00
v15 = (v15 << 8) | (v15 >> 24)
v10 += v15
v05 ^= v10
v05 = (v05 << 7) | (v05 >> 25)
v01 += v06
v12 ^= v01
v12 = (v12 << 16) | (v12 >> 16)
v11 += v12
v06 ^= v11
v06 = (v06 << 12) | (v06 >> 20)
v01 += v06
v12 ^= v01
v12 = (v12 << 8) | (v12 >> 24)
v11 += v12
v06 ^= v11
v06 = (v06 << 7) | (v06 >> 25)
v02 += v07
v13 ^= v02
v13 = (v13 << 16) | (v13 >> 16)
v08 += v13
v07 ^= v08
v07 = (v07 << 12) | (v07 >> 20)
v02 += v07
v13 ^= v02
v13 = (v13 << 8) | (v13 >> 24)
v08 += v13
v07 ^= v08
v07 = (v07 << 7) | (v07 >> 25)
v03 += v04
v14 ^= v03
v14 = (v14 << 16) | (v14 >> 16)
v09 += v14
v04 ^= v09
v04 = (v04 << 12) | (v04 >> 20)
v03 += v04
v14 ^= v03
v14 = (v14 << 8) | (v14 >> 24)
v09 += v14
v04 ^= v09
v04 = (v04 << 7) | (v04 >> 25)
}
v00 += s00
v01 += s01
v02 += s02
v03 += s03
v04 += s04
v05 += s05
v06 += s06
v07 += s07
v08 += s08
v09 += s09
v10 += s10
v11 += s11
v12 += s12
v13 += s13
v14 += s14
v15 += s15
s12++
binary.LittleEndian.PutUint32(state[48:], s12)
if s12 == 0 { // indicates overflow
s13++
binary.LittleEndian.PutUint32(state[52:], s13)
}
binary.LittleEndian.PutUint32(dst[0:], v00)
binary.LittleEndian.PutUint32(dst[4:], v01)
binary.LittleEndian.PutUint32(dst[8:], v02)
binary.LittleEndian.PutUint32(dst[12:], v03)
binary.LittleEndian.PutUint32(dst[16:], v04)
binary.LittleEndian.PutUint32(dst[20:], v05)
binary.LittleEndian.PutUint32(dst[24:], v06)
binary.LittleEndian.PutUint32(dst[28:], v07)
binary.LittleEndian.PutUint32(dst[32:], v08)
binary.LittleEndian.PutUint32(dst[36:], v09)
binary.LittleEndian.PutUint32(dst[40:], v10)
binary.LittleEndian.PutUint32(dst[44:], v11)
binary.LittleEndian.PutUint32(dst[48:], v12)
binary.LittleEndian.PutUint32(dst[52:], v13)
binary.LittleEndian.PutUint32(dst[56:], v14)
binary.LittleEndian.PutUint32(dst[60:], v15)
}
func hChaCha20Generic(out *[32]byte, nonce *[16]byte, key *[32]byte) {
v00 := sigma[0]
v01 := sigma[1]
v02 := sigma[2]
v03 := sigma[3]
v04 := binary.LittleEndian.Uint32(key[0:])
v05 := binary.LittleEndian.Uint32(key[4:])
v06 := binary.LittleEndian.Uint32(key[8:])
v07 := binary.LittleEndian.Uint32(key[12:])
v08 := binary.LittleEndian.Uint32(key[16:])
v09 := binary.LittleEndian.Uint32(key[20:])
v10 := binary.LittleEndian.Uint32(key[24:])
v11 := binary.LittleEndian.Uint32(key[28:])
v12 := binary.LittleEndian.Uint32(nonce[0:])
v13 := binary.LittleEndian.Uint32(nonce[4:])
v14 := binary.LittleEndian.Uint32(nonce[8:])
v15 := binary.LittleEndian.Uint32(nonce[12:])
for i := 0; i < 20; i += 2 {
v00 += v04
v12 ^= v00
v12 = (v12 << 16) | (v12 >> 16)
v08 += v12
v04 ^= v08
v04 = (v04 << 12) | (v04 >> 20)
v00 += v04
v12 ^= v00
v12 = (v12 << 8) | (v12 >> 24)
v08 += v12
v04 ^= v08
v04 = (v04 << 7) | (v04 >> 25)
v01 += v05
v13 ^= v01
v13 = (v13 << 16) | (v13 >> 16)
v09 += v13
v05 ^= v09
v05 = (v05 << 12) | (v05 >> 20)
v01 += v05
v13 ^= v01
v13 = (v13 << 8) | (v13 >> 24)
v09 += v13
v05 ^= v09
v05 = (v05 << 7) | (v05 >> 25)
v02 += v06
v14 ^= v02
v14 = (v14 << 16) | (v14 >> 16)
v10 += v14
v06 ^= v10
v06 = (v06 << 12) | (v06 >> 20)
v02 += v06
v14 ^= v02
v14 = (v14 << 8) | (v14 >> 24)
v10 += v14
v06 ^= v10
v06 = (v06 << 7) | (v06 >> 25)
v03 += v07
v15 ^= v03
v15 = (v15 << 16) | (v15 >> 16)
v11 += v15
v07 ^= v11
v07 = (v07 << 12) | (v07 >> 20)
v03 += v07
v15 ^= v03
v15 = (v15 << 8) | (v15 >> 24)
v11 += v15
v07 ^= v11
v07 = (v07 << 7) | (v07 >> 25)
v00 += v05
v15 ^= v00
v15 = (v15 << 16) | (v15 >> 16)
v10 += v15
v05 ^= v10
v05 = (v05 << 12) | (v05 >> 20)
v00 += v05
v15 ^= v00
v15 = (v15 << 8) | (v15 >> 24)
v10 += v15
v05 ^= v10
v05 = (v05 << 7) | (v05 >> 25)
v01 += v06
v12 ^= v01
v12 = (v12 << 16) | (v12 >> 16)
v11 += v12
v06 ^= v11
v06 = (v06 << 12) | (v06 >> 20)
v01 += v06
v12 ^= v01
v12 = (v12 << 8) | (v12 >> 24)
v11 += v12
v06 ^= v11
v06 = (v06 << 7) | (v06 >> 25)
v02 += v07
v13 ^= v02
v13 = (v13 << 16) | (v13 >> 16)
v08 += v13
v07 ^= v08
v07 = (v07 << 12) | (v07 >> 20)
v02 += v07
v13 ^= v02
v13 = (v13 << 8) | (v13 >> 24)
v08 += v13
v07 ^= v08
v07 = (v07 << 7) | (v07 >> 25)
v03 += v04
v14 ^= v03
v14 = (v14 << 16) | (v14 >> 16)
v09 += v14
v04 ^= v09
v04 = (v04 << 12) | (v04 >> 20)
v03 += v04
v14 ^= v03
v14 = (v14 << 8) | (v14 >> 24)
v09 += v14
v04 ^= v09
v04 = (v04 << 7) | (v04 >> 25)
}
binary.LittleEndian.PutUint32(out[0:], v00)
binary.LittleEndian.PutUint32(out[4:], v01)
binary.LittleEndian.PutUint32(out[8:], v02)
binary.LittleEndian.PutUint32(out[12:], v03)
binary.LittleEndian.PutUint32(out[16:], v12)
binary.LittleEndian.PutUint32(out[20:], v13)
binary.LittleEndian.PutUint32(out[24:], v14)
binary.LittleEndian.PutUint32(out[28:], v15)
}

33
vendor/github.com/aead/chacha20/chacha/chacha_ref.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build !amd64,!386 gccgo appengine nacl
package chacha
import "encoding/binary"
func init() {
useSSE2 = false
useSSSE3 = false
useAVX = false
useAVX2 = false
}
func initialize(state *[64]byte, key []byte, nonce *[16]byte) {
binary.LittleEndian.PutUint32(state[0:], sigma[0])
binary.LittleEndian.PutUint32(state[4:], sigma[1])
binary.LittleEndian.PutUint32(state[8:], sigma[2])
binary.LittleEndian.PutUint32(state[12:], sigma[3])
copy(state[16:], key[:])
copy(state[48:], nonce[:])
}
func xorKeyStream(dst, src []byte, block, state *[64]byte, rounds int) int {
return xorKeyStreamGeneric(dst, src, block, state, rounds)
}
func hChaCha20(out *[32]byte, nonce *[16]byte, key *[32]byte) {
hChaCha20Generic(out, nonce, key)
}

53
vendor/github.com/aead/chacha20/chacha/const.s generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl
#include "textflag.h"
DATA ·sigma<>+0x00(SB)/4, $0x61707865
DATA ·sigma<>+0x04(SB)/4, $0x3320646e
DATA ·sigma<>+0x08(SB)/4, $0x79622d32
DATA ·sigma<>+0x0C(SB)/4, $0x6b206574
GLOBL ·sigma<>(SB), (NOPTR+RODATA), $16 // The 4 ChaCha initialization constants
// SSE2/SSE3/AVX constants
DATA ·one<>+0x00(SB)/8, $1
DATA ·one<>+0x08(SB)/8, $0
GLOBL ·one<>(SB), (NOPTR+RODATA), $16 // The constant 1 as 128 bit value
DATA ·rol16<>+0x00(SB)/8, $0x0504070601000302
DATA ·rol16<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
GLOBL ·rol16<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 16 bit left rotate constant
DATA ·rol8<>+0x00(SB)/8, $0x0605040702010003
DATA ·rol8<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
GLOBL ·rol8<>(SB), (NOPTR+RODATA), $16 // The PSHUFB 8 bit left rotate constant
// AVX2 constants
DATA ·one_AVX2<>+0x00(SB)/8, $0
DATA ·one_AVX2<>+0x08(SB)/8, $0
DATA ·one_AVX2<>+0x10(SB)/8, $1
DATA ·one_AVX2<>+0x18(SB)/8, $0
GLOBL ·one_AVX2<>(SB), (NOPTR+RODATA), $32 // The constant 1 as 256 bit value
DATA ·two_AVX2<>+0x00(SB)/8, $2
DATA ·two_AVX2<>+0x08(SB)/8, $0
DATA ·two_AVX2<>+0x10(SB)/8, $2
DATA ·two_AVX2<>+0x18(SB)/8, $0
GLOBL ·two_AVX2<>(SB), (NOPTR+RODATA), $32
DATA ·rol16_AVX2<>+0x00(SB)/8, $0x0504070601000302
DATA ·rol16_AVX2<>+0x08(SB)/8, $0x0D0C0F0E09080B0A
DATA ·rol16_AVX2<>+0x10(SB)/8, $0x0504070601000302
DATA ·rol16_AVX2<>+0x18(SB)/8, $0x0D0C0F0E09080B0A
GLOBL ·rol16_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 16 bit left rotate constant
DATA ·rol8_AVX2<>+0x00(SB)/8, $0x0605040702010003
DATA ·rol8_AVX2<>+0x08(SB)/8, $0x0E0D0C0F0A09080B
DATA ·rol8_AVX2<>+0x10(SB)/8, $0x0605040702010003
DATA ·rol8_AVX2<>+0x18(SB)/8, $0x0E0D0C0F0A09080B
GLOBL ·rol8_AVX2<>(SB), (NOPTR+RODATA), $32 // The VPSHUFB 8 bit left rotate constant

163
vendor/github.com/aead/chacha20/chacha/macro.s generated vendored Normal file
View File

@ -0,0 +1,163 @@
// Copyright (c) 2018 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// +build 386,!gccgo,!appengine,!nacl amd64,!gccgo,!appengine,!nacl
// ROTL_SSE rotates all 4 32 bit values of the XMM register v
// left by n bits using SSE2 instructions (0 <= n <= 32).
// The XMM register t is used as a temp. register.
#define ROTL_SSE(n, t, v) \
MOVO v, t; \
PSLLL $n, t; \
PSRLL $(32-n), v; \
PXOR t, v
// ROTL_AVX rotates all 4/8 32 bit values of the AVX/AVX2 register v
// left by n bits using AVX/AVX2 instructions (0 <= n <= 32).
// The AVX/AVX2 register t is used as a temp. register.
#define ROTL_AVX(n, t, v) \
VPSLLD $n, v, t; \
VPSRLD $(32-n), v, v; \
VPXOR v, t, v
// CHACHA_QROUND_SSE2 performs a ChaCha quarter-round using the
// 4 XMM registers v0, v1, v2 and v3. It uses only ROTL_SSE2 for
// rotations. The XMM register t is used as a temp. register.
#define CHACHA_QROUND_SSE2(v0, v1, v2, v3, t) \
PADDL v1, v0; \
PXOR v0, v3; \
ROTL_SSE(16, t, v3); \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE(12, t, v1); \
PADDL v1, v0; \
PXOR v0, v3; \
ROTL_SSE(8, t, v3); \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE(7, t, v1)
// CHACHA_QROUND_SSSE3 performs a ChaCha quarter-round using the
// 4 XMM registers v0, v1, v2 and v3. It uses PSHUFB for 8/16 bit
// rotations. The XMM register t is used as a temp. register.
//
// r16 holds the PSHUFB constant for a 16 bit left rotate.
// r8 holds the PSHUFB constant for a 8 bit left rotate.
#define CHACHA_QROUND_SSSE3(v0, v1, v2, v3, t, r16, r8) \
PADDL v1, v0; \
PXOR v0, v3; \
PSHUFB r16, v3; \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE(12, t, v1); \
PADDL v1, v0; \
PXOR v0, v3; \
PSHUFB r8, v3; \
PADDL v3, v2; \
PXOR v2, v1; \
ROTL_SSE(7, t, v1)
// CHACHA_QROUND_AVX performs a ChaCha quarter-round using the
// 4 AVX/AVX2 registers v0, v1, v2 and v3. It uses VPSHUFB for 8/16 bit
// rotations. The AVX/AVX2 register t is used as a temp. register.
//
// r16 holds the VPSHUFB constant for a 16 bit left rotate.
// r8 holds the VPSHUFB constant for a 8 bit left rotate.
#define CHACHA_QROUND_AVX(v0, v1, v2, v3, t, r16, r8) \
VPADDD v0, v1, v0; \
VPXOR v3, v0, v3; \
VPSHUFB r16, v3, v3; \
VPADDD v2, v3, v2; \
VPXOR v1, v2, v1; \
ROTL_AVX(12, t, v1); \
VPADDD v0, v1, v0; \
VPXOR v3, v0, v3; \
VPSHUFB r8, v3, v3; \
VPADDD v2, v3, v2; \
VPXOR v1, v2, v1; \
ROTL_AVX(7, t, v1)
// CHACHA_SHUFFLE_SSE performs a ChaCha shuffle using the
// 3 XMM registers v1, v2 and v3. The inverse shuffle is
// performed by switching v1 and v3: CHACHA_SHUFFLE_SSE(v3, v2, v1).
#define CHACHA_SHUFFLE_SSE(v1, v2, v3) \
PSHUFL $0x39, v1, v1; \
PSHUFL $0x4E, v2, v2; \
PSHUFL $0x93, v3, v3
// CHACHA_SHUFFLE_AVX performs a ChaCha shuffle using the
// 3 AVX/AVX2 registers v1, v2 and v3. The inverse shuffle is
// performed by switching v1 and v3: CHACHA_SHUFFLE_AVX(v3, v2, v1).
#define CHACHA_SHUFFLE_AVX(v1, v2, v3) \
VPSHUFD $0x39, v1, v1; \
VPSHUFD $0x4E, v2, v2; \
VPSHUFD $0x93, v3, v3
// XOR_SSE extracts 4x16 byte vectors from src at
// off, xors all vectors with the corresponding XMM
// register (v0 - v3) and writes the result to dst
// at off.
// The XMM register t is used as a temp. register.
#define XOR_SSE(dst, src, off, v0, v1, v2, v3, t) \
MOVOU 0+off(src), t; \
PXOR v0, t; \
MOVOU t, 0+off(dst); \
MOVOU 16+off(src), t; \
PXOR v1, t; \
MOVOU t, 16+off(dst); \
MOVOU 32+off(src), t; \
PXOR v2, t; \
MOVOU t, 32+off(dst); \
MOVOU 48+off(src), t; \
PXOR v3, t; \
MOVOU t, 48+off(dst)
// XOR_AVX extracts 4x16 byte vectors from src at
// off, xors all vectors with the corresponding AVX
// register (v0 - v3) and writes the result to dst
// at off.
// The XMM register t is used as a temp. register.
#define XOR_AVX(dst, src, off, v0, v1, v2, v3, t) \
VPXOR 0+off(src), v0, t; \
VMOVDQU t, 0+off(dst); \
VPXOR 16+off(src), v1, t; \
VMOVDQU t, 16+off(dst); \
VPXOR 32+off(src), v2, t; \
VMOVDQU t, 32+off(dst); \
VPXOR 48+off(src), v3, t; \
VMOVDQU t, 48+off(dst)
#define XOR_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
VMOVDQU (0+off)(src), t0; \
VPERM2I128 $32, v1, v0, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (0+off)(dst); \
VMOVDQU (32+off)(src), t0; \
VPERM2I128 $32, v3, v2, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (32+off)(dst); \
VMOVDQU (64+off)(src), t0; \
VPERM2I128 $49, v1, v0, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (64+off)(dst); \
VMOVDQU (96+off)(src), t0; \
VPERM2I128 $49, v3, v2, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (96+off)(dst)
#define XOR_UPPER_AVX2(dst, src, off, v0, v1, v2, v3, t0, t1) \
VMOVDQU (0+off)(src), t0; \
VPERM2I128 $32, v1, v0, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (0+off)(dst); \
VMOVDQU (32+off)(src), t0; \
VPERM2I128 $32, v3, v2, t1; \
VPXOR t0, t1, t0; \
VMOVDQU t0, (32+off)(dst); \
#define EXTRACT_LOWER(dst, v0, v1, v2, v3, t0) \
VPERM2I128 $49, v1, v0, t0; \
VMOVDQU t0, 0(dst); \
VPERM2I128 $49, v3, v2, t0; \
VMOVDQU t0, 32(dst)

41
vendor/github.com/aead/chacha20/chacha20.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.
// Package chacha20 implements the ChaCha20 / XChaCha20 stream chipher.
// Notice that one specific key-nonce combination must be unique for all time.
//
// There are three versions of ChaCha20:
// - ChaCha20 with a 64 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
// - ChaCha20 with a 96 bit nonce (en/decrypt up to 2^32 * 64 bytes (~256 GB) for one key-nonce combination)
// - XChaCha20 with a 192 bit nonce (en/decrypt up to 2^64 * 64 bytes for one key-nonce combination)
package chacha20 // import "github.com/aead/chacha20"
import (
"crypto/cipher"
"github.com/aead/chacha20/chacha"
)
// XORKeyStream crypts bytes from src to dst using the given nonce and key.
// The length of the nonce determinds the version of ChaCha20:
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
// Src and dst may be the same slice but otherwise should not overlap.
// If len(dst) < len(src) this function panics.
// If the nonce is neither 64, 96 nor 192 bits long, this function panics.
func XORKeyStream(dst, src, nonce, key []byte) {
chacha.XORKeyStream(dst, src, nonce, key, 20)
}
// NewCipher returns a new cipher.Stream implementing a ChaCha20 version.
// The nonce must be unique for one key for all time.
// The length of the nonce determinds the version of ChaCha20:
// - 8 bytes: ChaCha20 with a 64 bit nonce and a 2^64 * 64 byte period.
// - 12 bytes: ChaCha20 as defined in RFC 7539 and a 2^32 * 64 byte period.
// - 24 bytes: XChaCha20 with a 192 bit nonce and a 2^64 * 64 byte period.
// If the nonce is neither 64, 96 nor 192 bits long, a non-nil error is returned.
func NewCipher(nonce, key []byte) (cipher.Stream, error) {
return chacha.NewCipher(nonce, key, 20)
}

View File

@ -1149,18 +1149,9 @@ func (b *BlockChain) initChainState() error {
blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
// Determine how many blocks will be loaded into the index so we can
// allocate the right amount.
var blockCount int32
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
blockCount++
}
blockNodes := make([]blockNode, blockCount)
var i int32
var lastNode *blockNode
cursor = blockIndexBucket.Cursor()
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
header, status, err := deserializeBlockRow(cursor.Value())
if err != nil {
@ -1193,7 +1184,7 @@ func (b *BlockChain) initChainState() error {
// Initialize the block node for the block, connect it,
// and add it to the block index.
node := &blockNodes[i]
node := new(blockNode)
initBlockNode(node, header, parent)
node.status = status
b.index.addNode(node)

View File

@ -111,6 +111,22 @@ func (entry *UtxoEntry) Clone() *UtxoEntry {
}
}
// NewUtxoEntry returns a new UtxoEntry built from the arguments.
func NewUtxoEntry(
txOut *wire.TxOut, blockHeight int32, isCoinbase bool) *UtxoEntry {
var cbFlag txoFlags
if isCoinbase {
cbFlag |= tfCoinBase
}
return &UtxoEntry{
amount: txOut.Value,
pkScript: txOut.PkScript,
blockHeight: blockHeight,
packedFlags: cbFlag,
}
}
// UtxoViewpoint represents a view into the set of unspent transaction outputs
// from a specific point of view in the chain. For example, it could be for
// the end of the main chain, some point in the history of the main chain, or

View File

@ -226,20 +226,24 @@ func (f *fieldVal) SetBytes(b *[32]byte) *fieldVal {
return f
}
// SetByteSlice packs the passed big-endian value into the internal field value
// representation. Only the first 32-bytes are used. As a result, it is up to
// the caller to ensure numbers of the appropriate size are used or the value
// will be truncated.
// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned
// integer (meaning it is truncated to the first 32 bytes), packs it into the
// internal field value representation, and returns the updated field value.
//
// Note that since passing a slice with more than 32 bytes is truncated, it is
// possible that the truncated value is less than the field prime. It is up to
// the caller to decide whether it needs to provide numbers of the appropriate
// size or if it is acceptable to use this function with the described
// truncation behavior.
//
// The field value is returned to support chaining. This enables syntax like:
// f := new(fieldVal).SetByteSlice(byteSlice)
func (f *fieldVal) SetByteSlice(b []byte) *fieldVal {
var b32 [32]byte
for i := 0; i < len(b); i++ {
if i < 32 {
b32[i+(32-len(b))] = b[i]
}
if len(b) > 32 {
b = b[:32]
}
copy(b32[32-len(b):], b)
return f.SetBytes(&b32)
}

View File

@ -59,6 +59,23 @@ func NewDebugLevelCmd(levelSpec string) *DebugLevelCmd {
}
}
// GenerateToAddressCmd defines the generatetoaddress JSON-RPC command.
type GenerateToAddressCmd struct {
NumBlocks int64
Address string
MaxTries *int64 `jsonrpcdefault:"1000000"`
}
// NewGenerateToAddressCmd returns a new instance which can be used to issue a
// generatetoaddress JSON-RPC command.
func NewGenerateToAddressCmd(numBlocks int64, address string, maxTries *int64) *GenerateToAddressCmd {
return &GenerateToAddressCmd{
NumBlocks: numBlocks,
Address: address,
MaxTries: maxTries,
}
}
// GenerateCmd defines the generate JSON-RPC command.
type GenerateCmd struct {
NumBlocks uint32
@ -131,6 +148,7 @@ func init() {
MustRegisterCmd("debuglevel", (*DebugLevelCmd)(nil), flags)
MustRegisterCmd("node", (*NodeCmd)(nil), flags)
MustRegisterCmd("generate", (*GenerateCmd)(nil), flags)
MustRegisterCmd("generatetoaddress", (*GenerateToAddressCmd)(nil), flags)
MustRegisterCmd("getbestblock", (*GetBestBlockCmd)(nil), flags)
MustRegisterCmd("getcurrentnet", (*GetCurrentNetCmd)(nil), flags)
MustRegisterCmd("getheaders", (*GetHeadersCmd)(nil), flags)

View File

@ -8,6 +8,7 @@
package btcjson
import (
"encoding/hex"
"encoding/json"
"fmt"
@ -63,10 +64,15 @@ type CreateRawTransactionCmd struct {
// NewCreateRawTransactionCmd returns a new instance which can be used to issue
// a createrawtransaction JSON-RPC command.
//
// Amounts are in BTC.
// Amounts are in BTC. Passing in nil and the empty slice as inputs is equivalent,
// both gets interpreted as the empty slice.
func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64,
lockTime *int64) *CreateRawTransactionCmd {
// to make sure we're serializing this to the empty list and not null, we
// explicitly initialize the list
if inputs == nil {
inputs = []TransactionInput{}
}
return &CreateRawTransactionCmd{
Inputs: inputs,
Amounts: amounts,
@ -74,6 +80,37 @@ func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]fl
}
}
// FundRawTransactionOpts are the different options that can be passed to rawtransaction
type FundRawTransactionOpts struct {
ChangeAddress *string `json:"changeAddress,omitempty"`
ChangePosition *int `json:"changePosition,omitempty"`
ChangeType *string `json:"change_type,omitempty"`
IncludeWatching *bool `json:"includeWatching,omitempty"`
LockUnspents *bool `json:"lockUnspents,omitempty"`
FeeRate *float64 `json:"feeRate,omitempty"` // BTC/kB
SubtractFeeFromOutputs []int `json:"subtractFeeFromOutputs,omitempty"`
Replaceable *bool `json:"replaceable,omitempty"`
ConfTarget *int `json:"conf_target,omitempty"`
EstimateMode *EstimateSmartFeeMode `json:"estimate_mode,omitempty"`
}
// FundRawTransactionCmd defines the fundrawtransaction JSON-RPC command
type FundRawTransactionCmd struct {
HexTx string
Options FundRawTransactionOpts
IsWitness *bool
}
// NewFundRawTransactionCmd returns a new instance which can be used to issue
// a fundrawtransaction JSON-RPC command
func NewFundRawTransactionCmd(serializedTx []byte, opts FundRawTransactionOpts, isWitness *bool) *FundRawTransactionCmd {
return &FundRawTransactionCmd{
HexTx: hex.EncodeToString(serializedTx),
Options: opts,
IsWitness: isWitness,
}
}
// DecodeRawTransactionCmd defines the decoderawtransaction JSON-RPC command.
type DecodeRawTransactionCmd struct {
HexTx string
@ -130,8 +167,7 @@ func NewGetBestBlockHashCmd() *GetBestBlockHashCmd {
// GetBlockCmd defines the getblock JSON-RPC command.
type GetBlockCmd struct {
Hash string
Verbose *bool `jsonrpcdefault:"true"`
VerboseTx *bool `jsonrpcdefault:"false"`
Verbosity *int `jsonrpcdefault:"1"`
}
// NewGetBlockCmd returns a new instance which can be used to issue a getblock
@ -139,11 +175,10 @@ type GetBlockCmd struct {
//
// 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 {
func NewGetBlockCmd(hash string, verbosity *int) *GetBlockCmd {
return &GetBlockCmd{
Hash: hash,
Verbose: verbose,
VerboseTx: verboseTx,
Verbosity: verbosity,
}
}
@ -193,6 +228,50 @@ func NewGetBlockHeaderCmd(hash string, verbose *bool) *GetBlockHeaderCmd {
}
}
// HashOrHeight defines a type that can be used as hash_or_height value in JSON-RPC commands.
type HashOrHeight struct {
Value interface{}
}
// MarshalJSON implements the json.Marshaler interface
func (h HashOrHeight) MarshalJSON() ([]byte, error) {
return json.Marshal(h.Value)
}
// UnmarshalJSON implements the json.Unmarshaler interface
func (h *HashOrHeight) UnmarshalJSON(data []byte) error {
var unmarshalled interface{}
if err := json.Unmarshal(data, &unmarshalled); err != nil {
return err
}
switch v := unmarshalled.(type) {
case float64:
h.Value = int(v)
case string:
h.Value = v
default:
return fmt.Errorf("invalid hash_or_height value: %v", unmarshalled)
}
return nil
}
// GetBlockStatsCmd defines the getblockstats JSON-RPC command.
type GetBlockStatsCmd struct {
HashOrHeight HashOrHeight
Stats *[]string
}
// NewGetBlockStatsCmd returns a new instance which can be used to issue a
// getblockstats JSON-RPC command. Either height or hash must be specified.
func NewGetBlockStatsCmd(hashOrHeight HashOrHeight, stats *[]string) *GetBlockStatsCmd {
return &GetBlockStatsCmd{
HashOrHeight: hashOrHeight,
Stats: stats,
}
}
// 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.
@ -321,6 +400,24 @@ func NewGetChainTipsCmd() *GetChainTipsCmd {
return &GetChainTipsCmd{}
}
// GetChainTxStatsCmd defines the getchaintxstats JSON-RPC command.
type GetChainTxStatsCmd struct {
NBlocks *int32
BlockHash *string
}
// NewGetChainTxStatsCmd returns a new instance which can be used to issue a
// getchaintxstats JSON-RPC command.
//
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
func NewGetChainTxStatsCmd(nBlocks *int32, blockHash *string) *GetChainTxStatsCmd {
return &GetChainTxStatsCmd{
NBlocks: nBlocks,
BlockHash: blockHash,
}
}
// GetConnectionCountCmd defines the getconnectioncount JSON-RPC command.
type GetConnectionCountCmd struct{}
@ -791,6 +888,7 @@ func init() {
MustRegisterCmd("addnode", (*AddNodeCmd)(nil), flags)
MustRegisterCmd("createrawtransaction", (*CreateRawTransactionCmd)(nil), flags)
MustRegisterCmd("fundrawtransaction", (*FundRawTransactionCmd)(nil), flags)
MustRegisterCmd("decoderawtransaction", (*DecodeRawTransactionCmd)(nil), flags)
MustRegisterCmd("decodescript", (*DecodeScriptCmd)(nil), flags)
MustRegisterCmd("getaddednodeinfo", (*GetAddedNodeInfoCmd)(nil), flags)
@ -800,10 +898,12 @@ func init() {
MustRegisterCmd("getblockcount", (*GetBlockCountCmd)(nil), flags)
MustRegisterCmd("getblockhash", (*GetBlockHashCmd)(nil), flags)
MustRegisterCmd("getblockheader", (*GetBlockHeaderCmd)(nil), flags)
MustRegisterCmd("getblockstats", (*GetBlockStatsCmd)(nil), flags)
MustRegisterCmd("getblocktemplate", (*GetBlockTemplateCmd)(nil), flags)
MustRegisterCmd("getcfilter", (*GetCFilterCmd)(nil), flags)
MustRegisterCmd("getcfilterheader", (*GetCFilterHeaderCmd)(nil), flags)
MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags)
MustRegisterCmd("getchaintxstats", (*GetChainTxStatsCmd)(nil), flags)
MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags)
MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags)
MustRegisterCmd("getgenerate", (*GetGenerateCmd)(nil), flags)

View File

@ -4,7 +4,14 @@
package btcjson
import "encoding/json"
import (
"bytes"
"encoding/hex"
"encoding/json"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// GetBlockHeaderVerboseResult models the data from the getblockheader command when
// the verbose flag is set. When the verbose flag is not set, getblockheader
@ -24,9 +31,44 @@ type GetBlockHeaderVerboseResult struct {
NextHash string `json:"nextblockhash,omitempty"`
}
// GetBlockStatsResult models the data from the getblockstats command.
type GetBlockStatsResult struct {
AverageFee int64 `json:"avgfee"`
AverageFeeRate int64 `json:"avgfeerate"`
AverageTxSize int64 `json:"avgtxsize"`
FeeratePercentiles []int64 `json:"feerate_percentiles"`
Hash string `json:"blockhash"`
Height int64 `json:"height"`
Ins int64 `json:"ins"`
MaxFee int64 `json:"maxfee"`
MaxFeeRate int64 `json:"maxfeerate"`
MaxTxSize int64 `json:"maxtxsize"`
MedianFee int64 `json:"medianfee"`
MedianTime int64 `json:"mediantime"`
MedianTxSize int64 `json:"mediantxsize"`
MinFee int64 `json:"minfee"`
MinFeeRate int64 `json:"minfeerate"`
MinTxSize int64 `json:"mintxsize"`
Outs int64 `json:"outs"`
SegWitTotalSize int64 `json:"swtotal_size"`
SegWitTotalWeight int64 `json:"swtotal_weight"`
SegWitTxs int64 `json:"swtxs"`
Subsidy int64 `json:"subsidy"`
Time int64 `json:"time"`
TotalOut int64 `json:"total_out"`
TotalSize int64 `json:"total_size"`
TotalWeight int64 `json:"total_weight"`
Txs int64 `json:"txs"`
UTXOIncrease int64 `json:"utxo_increase"`
UTXOSizeIncrease int64 `json:"utxo_size_inc"`
}
// 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.
// verbose flag is set to 1. When the verbose flag is set to 0, getblock returns a
// hex-encoded string. When the verbose flag is set to 1, getblock returns an object
// whose tx field is an array of transaction hashes. When the verbose flag is set to 2,
// getblock returns an object whose tx field is an array of raw transactions.
// Use GetBlockVerboseTxResult to unmarshal data received from passing verbose=2 to getblock.
type GetBlockVerboseResult struct {
Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"`
@ -38,7 +80,7 @@ type GetBlockVerboseResult struct {
VersionHex string `json:"versionHex"`
MerkleRoot string `json:"merkleroot"`
Tx []string `json:"tx,omitempty"`
RawTx []TxRawResult `json:"rawtx,omitempty"`
RawTx []TxRawResult `json:"rawtx,omitempty"` // Note: this field is always empty when verbose != 2.
Time int64 `json:"time"`
Nonce uint32 `json:"nonce"`
Bits string `json:"bits"`
@ -47,6 +89,43 @@ type GetBlockVerboseResult struct {
NextHash string `json:"nextblockhash,omitempty"`
}
// GetBlockVerboseTxResult models the data from the getblock command when the
// verbose flag is set to 2. When the verbose flag is set to 0, getblock returns a
// hex-encoded string. When the verbose flag is set to 1, getblock returns an object
// whose tx field is an array of transaction hashes. When the verbose flag is set to 2,
// getblock returns an object whose tx field is an array of raw transactions.
// Use GetBlockVerboseResult to unmarshal data received from passing verbose=1 to getblock.
type GetBlockVerboseTxResult 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 []TxRawResult `json:"tx,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"`
}
// GetChainTxStatsResult models the data from the getchaintxstats command.
type GetChainTxStatsResult struct {
Time int64 `json:"time"`
TxCount int64 `json:"txcount"`
WindowFinalBlockHash string `json:"window_final_block_hash"`
WindowFinalBlockHeight int32 `json:"window_final_block_height"`
WindowBlockCount int32 `json:"window_block_count"`
WindowTxCount int32 `json:"window_tx_count"`
WindowInterval int32 `json:"window_interval"`
TxRate float64 `json:"txrate"`
}
// CreateMultiSigResult models the data returned from the createmultisig
// command.
type CreateMultiSigResult struct {
@ -206,23 +285,35 @@ type GetBlockTemplateResult struct {
RejectReasion string `json:"reject-reason,omitempty"`
}
// GetMempoolEntryResult models the data returned from the getmempoolentry's
// fee field
type MempoolFees struct {
Base float64 `json:"base"`
Modified float64 `json:"modified"`
Ancestor float64 `json:"ancestor"`
Descendant float64 `json:"descendant"`
}
// 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"`
VSize int32 `json:"vsize"`
Size int32 `json:"size"`
Weight int64 `json:"weight"`
Fee float64 `json:"fee"`
ModifiedFee float64 `json:"modifiedfee"`
Time int64 `json:"time"`
Height int64 `json:"height"`
DescendantCount int64 `json:"descendantcount"`
DescendantSize int64 `json:"descendantsize"`
DescendantFees float64 `json:"descendantfees"`
AncestorCount int64 `json:"ancestorcount"`
AncestorSize int64 `json:"ancestorsize"`
AncestorFees float64 `json:"ancestorfees"`
WTxId string `json:"wtxid"`
Fees MempoolFees `json:"fees"`
Depends []string `json:"depends"`
}
// GetMempoolInfoResult models the data returned from the getmempoolinfo
@ -584,3 +675,58 @@ type ValidateAddressChainResult struct {
IsValid bool `json:"isvalid"`
Address string `json:"address,omitempty"`
}
// EstimateSmartFeeResult models the data returned buy the chain server
// estimatesmartfee command
type EstimateSmartFeeResult struct {
FeeRate *float64 `json:"feerate,omitempty"`
Errors []string `json:"errors,omitempty"`
Blocks int64 `json:"blocks"`
}
var _ json.Unmarshaler = &FundRawTransactionResult{}
type rawFundRawTransactionResult struct {
Transaction string `json:"hex"`
Fee float64 `json:"fee"`
ChangePosition int `json:"changepos"`
}
// FundRawTransactionResult is the result of the fundrawtransaction JSON-RPC call
type FundRawTransactionResult struct {
Transaction *wire.MsgTx
Fee btcutil.Amount
ChangePosition int // the position of the added change output, or -1
}
// UnmarshalJSON unmarshals the result of the fundrawtransaction JSON-RPC call
func (f *FundRawTransactionResult) UnmarshalJSON(data []byte) error {
var rawRes rawFundRawTransactionResult
if err := json.Unmarshal(data, &rawRes); err != nil {
return err
}
txBytes, err := hex.DecodeString(rawRes.Transaction)
if err != nil {
return err
}
var msgTx wire.MsgTx
witnessErr := msgTx.Deserialize(bytes.NewReader(txBytes))
if witnessErr != nil {
legacyErr := msgTx.DeserializeNoWitness(bytes.NewReader(txBytes))
if legacyErr != nil {
return legacyErr
}
}
fee, err := btcutil.NewAmount(rawRes.Fee)
if err != nil {
return err
}
f.Transaction = &msgTx
f.Fee = fee
f.ChangePosition = rawRes.ChangePosition
return nil
}

View File

@ -80,7 +80,7 @@ func NewStopNotifyNewTransactionsCmd() *StopNotifyNewTransactionsCmd {
// NotifyReceivedCmd defines the notifyreceived JSON-RPC command.
//
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
// Deprecated: Use LoadTxFilterCmd instead.
type NotifyReceivedCmd struct {
Addresses []string
}
@ -88,7 +88,7 @@ type NotifyReceivedCmd struct {
// NewNotifyReceivedCmd returns a new instance which can be used to issue a
// notifyreceived JSON-RPC command.
//
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
// Deprecated: Use NewLoadTxFilterCmd instead.
func NewNotifyReceivedCmd(addresses []string) *NotifyReceivedCmd {
return &NotifyReceivedCmd{
Addresses: addresses,
@ -128,7 +128,7 @@ func NewLoadTxFilterCmd(reload bool, addresses []string, outPoints []OutPoint) *
// NotifySpentCmd defines the notifyspent JSON-RPC command.
//
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
// Deprecated: Use LoadTxFilterCmd instead.
type NotifySpentCmd struct {
OutPoints []OutPoint
}
@ -136,7 +136,7 @@ type NotifySpentCmd struct {
// NewNotifySpentCmd returns a new instance which can be used to issue a
// notifyspent JSON-RPC command.
//
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
// Deprecated: Use NewLoadTxFilterCmd instead.
func NewNotifySpentCmd(outPoints []OutPoint) *NotifySpentCmd {
return &NotifySpentCmd{
OutPoints: outPoints,
@ -145,7 +145,7 @@ func NewNotifySpentCmd(outPoints []OutPoint) *NotifySpentCmd {
// StopNotifyReceivedCmd defines the stopnotifyreceived JSON-RPC command.
//
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
// Deprecated: Use LoadTxFilterCmd instead.
type StopNotifyReceivedCmd struct {
Addresses []string
}
@ -153,7 +153,7 @@ type StopNotifyReceivedCmd struct {
// NewStopNotifyReceivedCmd returns a new instance which can be used to issue a
// stopnotifyreceived JSON-RPC command.
//
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
// Deprecated: Use NewLoadTxFilterCmd instead.
func NewStopNotifyReceivedCmd(addresses []string) *StopNotifyReceivedCmd {
return &StopNotifyReceivedCmd{
Addresses: addresses,
@ -162,7 +162,7 @@ func NewStopNotifyReceivedCmd(addresses []string) *StopNotifyReceivedCmd {
// StopNotifySpentCmd defines the stopnotifyspent JSON-RPC command.
//
// NOTE: Deprecated. Use LoadTxFilterCmd instead.
// Deprecated: Use LoadTxFilterCmd instead.
type StopNotifySpentCmd struct {
OutPoints []OutPoint
}
@ -170,7 +170,7 @@ type StopNotifySpentCmd struct {
// NewStopNotifySpentCmd returns a new instance which can be used to issue a
// stopnotifyspent JSON-RPC command.
//
// NOTE: Deprecated. Use NewLoadTxFilterCmd instead.
// Deprecated: Use NewLoadTxFilterCmd instead.
func NewStopNotifySpentCmd(outPoints []OutPoint) *StopNotifySpentCmd {
return &StopNotifySpentCmd{
OutPoints: outPoints,
@ -179,7 +179,7 @@ func NewStopNotifySpentCmd(outPoints []OutPoint) *StopNotifySpentCmd {
// RescanCmd defines the rescan JSON-RPC command.
//
// NOTE: Deprecated. Use RescanBlocksCmd instead.
// Deprecated: Use RescanBlocksCmd instead.
type RescanCmd struct {
BeginBlock string
Addresses []string
@ -193,7 +193,7 @@ type RescanCmd struct {
// The parameters which are pointers indicate they are optional. Passing nil
// for optional parameters will use the default value.
//
// NOTE: Deprecated. Use NewRescanBlocksCmd instead.
// Deprecated: Use NewRescanBlocksCmd instead.
func NewRescanCmd(beginBlock string, addresses []string, outPoints []OutPoint, endBlock *string) *RescanCmd {
return &RescanCmd{
BeginBlock: beginBlock,

View File

@ -12,14 +12,14 @@ 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.
// 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.
// Deprecated: Use FilteredBlockDisconnectedNtfnMethod instead.
BlockDisconnectedNtfnMethod = "blockdisconnected"
// FilteredBlockConnectedNtfnMethod is the new method used for
@ -35,7 +35,7 @@ const (
// notifications from the chain server that a transaction which pays to
// a registered address has been processed.
//
// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
// Deprecated: Use RelevantTxAcceptedNtfnMethod and
// FilteredBlockConnectedNtfnMethod instead.
RecvTxNtfnMethod = "recvtx"
@ -43,7 +43,7 @@ const (
// notifications from the chain server that a transaction which spends a
// registered outpoint has been processed.
//
// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
// Deprecated: Use RelevantTxAcceptedNtfnMethod and
// FilteredBlockConnectedNtfnMethod instead.
RedeemingTxNtfnMethod = "redeemingtx"
@ -51,14 +51,14 @@ const (
// notifications from the chain server that a legacy, deprecated rescan
// operation has finished.
//
// NOTE: Deprecated. Not used with rescanblocks command.
// 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.
// Deprecated: Not used with rescanblocks command.
RescanProgressNtfnMethod = "rescanprogress"
// TxAcceptedNtfnMethod is the method used for notifications from the
@ -79,7 +79,7 @@ const (
// BlockConnectedNtfn defines the blockconnected JSON-RPC notification.
//
// NOTE: Deprecated. Use FilteredBlockConnectedNtfn instead.
// Deprecated: Use FilteredBlockConnectedNtfn instead.
type BlockConnectedNtfn struct {
Hash string
Height int32
@ -89,7 +89,7 @@ type BlockConnectedNtfn struct {
// NewBlockConnectedNtfn returns a new instance which can be used to issue a
// blockconnected JSON-RPC notification.
//
// NOTE: Deprecated. Use NewFilteredBlockConnectedNtfn instead.
// Deprecated: Use NewFilteredBlockConnectedNtfn instead.
func NewBlockConnectedNtfn(hash string, height int32, time int64) *BlockConnectedNtfn {
return &BlockConnectedNtfn{
Hash: hash,
@ -100,7 +100,7 @@ func NewBlockConnectedNtfn(hash string, height int32, time int64) *BlockConnecte
// BlockDisconnectedNtfn defines the blockdisconnected JSON-RPC notification.
//
// NOTE: Deprecated. Use FilteredBlockDisconnectedNtfn instead.
// Deprecated: Use FilteredBlockDisconnectedNtfn instead.
type BlockDisconnectedNtfn struct {
Hash string
Height int32
@ -110,7 +110,7 @@ type BlockDisconnectedNtfn struct {
// NewBlockDisconnectedNtfn returns a new instance which can be used to issue a
// blockdisconnected JSON-RPC notification.
//
// NOTE: Deprecated. Use NewFilteredBlockDisconnectedNtfn instead.
// Deprecated: Use NewFilteredBlockDisconnectedNtfn instead.
func NewBlockDisconnectedNtfn(hash string, height int32, time int64) *BlockDisconnectedNtfn {
return &BlockDisconnectedNtfn{
Hash: hash,
@ -163,7 +163,7 @@ type BlockDetails struct {
// RecvTxNtfn defines the recvtx JSON-RPC notification.
//
// NOTE: Deprecated. Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
// Deprecated: Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
// instead.
type RecvTxNtfn struct {
HexTx string
@ -173,7 +173,7 @@ type RecvTxNtfn struct {
// NewRecvTxNtfn returns a new instance which can be used to issue a recvtx
// JSON-RPC notification.
//
// NOTE: Deprecated. Use NewRelevantTxAcceptedNtfn and
// Deprecated: Use NewRelevantTxAcceptedNtfn and
// NewFilteredBlockConnectedNtfn instead.
func NewRecvTxNtfn(hexTx string, block *BlockDetails) *RecvTxNtfn {
return &RecvTxNtfn{
@ -184,7 +184,7 @@ func NewRecvTxNtfn(hexTx string, block *BlockDetails) *RecvTxNtfn {
// RedeemingTxNtfn defines the redeemingtx JSON-RPC notification.
//
// NOTE: Deprecated. Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
// Deprecated: Use RelevantTxAcceptedNtfn and FilteredBlockConnectedNtfn
// instead.
type RedeemingTxNtfn struct {
HexTx string
@ -194,7 +194,7 @@ type RedeemingTxNtfn struct {
// NewRedeemingTxNtfn returns a new instance which can be used to issue a
// redeemingtx JSON-RPC notification.
//
// NOTE: Deprecated. Use NewRelevantTxAcceptedNtfn and
// Deprecated: Use NewRelevantTxAcceptedNtfn and
// NewFilteredBlockConnectedNtfn instead.
func NewRedeemingTxNtfn(hexTx string, block *BlockDetails) *RedeemingTxNtfn {
return &RedeemingTxNtfn{
@ -205,7 +205,7 @@ func NewRedeemingTxNtfn(hexTx string, block *BlockDetails) *RedeemingTxNtfn {
// RescanFinishedNtfn defines the rescanfinished JSON-RPC notification.
//
// NOTE: Deprecated. Not used with rescanblocks command.
// Deprecated: Not used with rescanblocks command.
type RescanFinishedNtfn struct {
Hash string
Height int32
@ -215,7 +215,7 @@ type RescanFinishedNtfn struct {
// NewRescanFinishedNtfn returns a new instance which can be used to issue a
// rescanfinished JSON-RPC notification.
//
// NOTE: Deprecated. Not used with rescanblocks command.
// Deprecated: Not used with rescanblocks command.
func NewRescanFinishedNtfn(hash string, height int32, time int64) *RescanFinishedNtfn {
return &RescanFinishedNtfn{
Hash: hash,
@ -226,7 +226,7 @@ func NewRescanFinishedNtfn(hash string, height int32, time int64) *RescanFinishe
// RescanProgressNtfn defines the rescanprogress JSON-RPC notification.
//
// NOTE: Deprecated. Not used with rescanblocks command.
// Deprecated: Not used with rescanblocks command.
type RescanProgressNtfn struct {
Hash string
Height int32
@ -236,7 +236,7 @@ type RescanProgressNtfn struct {
// NewRescanProgressNtfn returns a new instance which can be used to issue a
// rescanprogress JSON-RPC notification.
//
// NOTE: Deprecated. Not used with rescanblocks command.
// Deprecated: Not used with rescanblocks command.
func NewRescanProgressNtfn(hash string, height int32, time int64) *RescanProgressNtfn {
return &RescanProgressNtfn{
Hash: hash,

View File

@ -22,10 +22,10 @@ type RPCError struct {
Message string `json:"message,omitempty"`
}
// Guarantee RPCError satisifies the builtin error interface.
// Guarantee RPCError satisfies the builtin error interface.
var _, _ error = RPCError{}, (*RPCError)(nil)
// Error returns a string describing the RPC error. This satisifies the
// Error returns a string describing the RPC error. This satisfies the
// builtin error interface.
func (e RPCError) Error() string {
return fmt.Sprintf("%d: %s", e.Code, e.Message)

View File

@ -39,6 +39,7 @@ const (
ErrRPCDatabase RPCErrorCode = -20
ErrRPCDeserialization RPCErrorCode = -22
ErrRPCVerify RPCErrorCode = -25
ErrRPCInWarmup RPCErrorCode = -28
)
// Peer-to-peer client errors.

View File

@ -287,6 +287,6 @@ func RegisteredCmdMethods() []string {
methods = append(methods, k)
}
sort.Sort(sort.StringSlice(methods))
sort.Strings(methods)
return methods
}

View File

@ -81,6 +81,30 @@ func NewEncryptWalletCmd(passphrase string) *EncryptWalletCmd {
}
}
// EstimateSmartFeeMode defines the different fee estimation modes available
// for the estimatesmartfee JSON-RPC command.
type EstimateSmartFeeMode string
var (
EstimateModeUnset EstimateSmartFeeMode = "UNSET"
EstimateModeEconomical EstimateSmartFeeMode = "ECONOMICAL"
EstimateModeConservative EstimateSmartFeeMode = "CONSERVATIVE"
)
// EstimateSmartFeeCmd defines the estimatesmartfee JSON-RPC command.
type EstimateSmartFeeCmd struct {
ConfTarget int64
EstimateMode *EstimateSmartFeeMode `jsonrpcdefault:"\"CONSERVATIVE\""`
}
// NewEstimateSmartFeeCmd returns a new instance which can be used to issue a
// estimatesmartfee JSON-RPC command.
func NewEstimateSmartFeeCmd(confTarget int64, mode *EstimateSmartFeeMode) *EstimateSmartFeeCmd {
return &EstimateSmartFeeCmd{
ConfTarget: confTarget, EstimateMode: mode,
}
}
// EstimateFeeCmd defines the estimatefee JSON-RPC command.
type EstimateFeeCmd struct {
NumBlocks int64
@ -164,6 +188,15 @@ func NewGetBalanceCmd(account *string, minConf *int) *GetBalanceCmd {
}
}
// GetBalancesCmd defines the getbalances JSON-RPC command.
type GetBalancesCmd struct{}
// NewGetBalancesCmd returns a new instance which can be used to issue a
// getbalances JSON-RPC command.
func NewGetBalancesCmd() *GetBalancesCmd {
return &GetBalancesCmd{}
}
// GetNewAddressCmd defines the getnewaddress JSON-RPC command.
type GetNewAddressCmd struct {
Account *string
@ -662,12 +695,14 @@ func init() {
MustRegisterCmd("createmultisig", (*CreateMultisigCmd)(nil), flags)
MustRegisterCmd("dumpprivkey", (*DumpPrivKeyCmd)(nil), flags)
MustRegisterCmd("encryptwallet", (*EncryptWalletCmd)(nil), flags)
MustRegisterCmd("estimatesmartfee", (*EstimateSmartFeeCmd)(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("getbalances", (*GetBalancesCmd)(nil), flags)
MustRegisterCmd("getnewaddress", (*GetNewAddressCmd)(nil), flags)
MustRegisterCmd("getrawchangeaddress", (*GetRawChangeAddressCmd)(nil), flags)
MustRegisterCmd("getreceivedbyaccount", (*GetReceivedByAccountCmd)(nil), flags)

View File

@ -159,3 +159,17 @@ type GetBestBlockResult struct {
Hash string `json:"hash"`
Height int32 `json:"height"`
}
// BalanceDetailsResult models the details data from the `getbalances` command.
type BalanceDetailsResult struct {
Trusted float64 `json:"trusted"`
UntrustedPending float64 `json:"untrusted_pending"`
Immature float64 `json:"immature"`
Used *float64 `json:"used"`
}
// GetBalancesResult models the data returned from the getbalances command.
type GetBalancesResult struct {
Mine BalanceDetailsResult `json:"mine"`
WatchOnly *BalanceDetailsResult `json:"watchonly"`
}

View File

@ -71,7 +71,7 @@ type DynamicBanScore struct {
func (s *DynamicBanScore) String() string {
s.mtx.Lock()
r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
s.persistent, s.transient, s.lastUnix, s.Int())
s.persistent, s.transient, s.lastUnix, s.int(time.Now()))
s.mtx.Unlock()
return r
}

View File

@ -0,0 +1,30 @@
rpctest
=======
[![Build Status](http://img.shields.io/travis/btcsuite/btcd.svg)](https://travis-ci.org/btcsuite/btcd)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcd/integration/rpctest)
Package rpctest provides a btcd-specific RPC testing harness crafting and
executing integration tests by driving a `btcd` instance via the `RPC`
interface. Each instance of an active harness comes equipped with a simple
in-memory HD wallet capable of properly syncing to the generated chain,
creating new addresses, and crafting fully signed transactions paying to an
arbitrary set of outputs.
This package was designed specifically to act as an RPC testing harness for
`btcd`. However, the constructs presented are general enough to be adapted to
any project wishing to programmatically drive a `btcd` instance of its
systems/integration tests.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcd/integration/rpctest
```
## License
Package rpctest is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@ -0,0 +1,207 @@
// 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 rpctest
import (
"errors"
"math"
"math/big"
"runtime"
"time"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// solveBlock attempts to find a nonce which makes the passed block header hash
// to a value less than the target difficulty. When a successful solution is
// found true is returned and the nonce field of the passed header is updated
// with the solution. False is returned if no solution exists.
func solveBlock(header *wire.BlockHeader, targetDifficulty *big.Int) bool {
// sbResult is used by the solver goroutines to send results.
type sbResult struct {
found bool
nonce uint32
}
// solver accepts a block header and a nonce range to test. It is
// intended to be run as a goroutine.
quit := make(chan bool)
results := make(chan sbResult)
solver := func(hdr wire.BlockHeader, startNonce, stopNonce uint32) {
// We need to modify the nonce field of the header, so make sure
// we work with a copy of the original header.
for i := startNonce; i >= startNonce && i <= stopNonce; i++ {
select {
case <-quit:
return
default:
hdr.Nonce = i
hash := hdr.BlockHash()
if blockchain.HashToBig(&hash).Cmp(targetDifficulty) <= 0 {
select {
case results <- sbResult{true, i}:
return
case <-quit:
return
}
}
}
}
select {
case results <- sbResult{false, 0}:
case <-quit:
return
}
}
startNonce := uint32(0)
stopNonce := uint32(math.MaxUint32)
numCores := uint32(runtime.NumCPU())
noncesPerCore := (stopNonce - startNonce) / numCores
for i := uint32(0); i < numCores; i++ {
rangeStart := startNonce + (noncesPerCore * i)
rangeStop := startNonce + (noncesPerCore * (i + 1)) - 1
if i == numCores-1 {
rangeStop = stopNonce
}
go solver(*header, rangeStart, rangeStop)
}
for i := uint32(0); i < numCores; i++ {
result := <-results
if result.found {
close(quit)
header.Nonce = result.nonce
return true
}
}
return false
}
// standardCoinbaseScript returns a standard script suitable for use as the
// signature script of the coinbase transaction of a new block. In particular,
// it starts with the block height that is required by version 2 blocks.
func standardCoinbaseScript(nextBlockHeight int32, extraNonce uint64) ([]byte, error) {
return txscript.NewScriptBuilder().AddInt64(int64(nextBlockHeight)).
AddInt64(int64(extraNonce)).Script()
}
// createCoinbaseTx returns a coinbase transaction paying an appropriate
// subsidy based on the passed block height to the provided address.
func createCoinbaseTx(coinbaseScript []byte, nextBlockHeight int32,
addr btcutil.Address, mineTo []wire.TxOut,
net *chaincfg.Params) (*btcutil.Tx, error) {
// Create the script to pay to the provided payment address.
pkScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return nil, err
}
tx := wire.NewMsgTx(wire.TxVersion)
tx.AddTxIn(&wire.TxIn{
// Coinbase transactions have no inputs, so previous outpoint is
// zero hash and max index.
PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
wire.MaxPrevOutIndex),
SignatureScript: coinbaseScript,
Sequence: wire.MaxTxInSequenceNum,
})
if len(mineTo) == 0 {
tx.AddTxOut(&wire.TxOut{
Value: blockchain.CalcBlockSubsidy(nextBlockHeight, net),
PkScript: pkScript,
})
} else {
for i := range mineTo {
tx.AddTxOut(&mineTo[i])
}
}
return btcutil.NewTx(tx), nil
}
// CreateBlock creates a new block building from the previous block with a
// specified blockversion and timestamp. If the timestamp passed is zero (not
// initialized), then the timestamp of the previous block will be used plus 1
// second is used. Passing nil for the previous block results in a block that
// builds off of the genesis block for the specified chain.
func CreateBlock(prevBlock *btcutil.Block, inclusionTxs []*btcutil.Tx,
blockVersion int32, blockTime time.Time, miningAddr btcutil.Address,
mineTo []wire.TxOut, net *chaincfg.Params) (*btcutil.Block, error) {
var (
prevHash *chainhash.Hash
blockHeight int32
prevBlockTime time.Time
)
// If the previous block isn't specified, then we'll construct a block
// that builds off of the genesis block for the chain.
if prevBlock == nil {
prevHash = net.GenesisHash
blockHeight = 1
prevBlockTime = net.GenesisBlock.Header.Timestamp.Add(time.Minute)
} else {
prevHash = prevBlock.Hash()
blockHeight = prevBlock.Height() + 1
prevBlockTime = prevBlock.MsgBlock().Header.Timestamp
}
// If a target block time was specified, then use that as the header's
// timestamp. Otherwise, add one second to the previous block unless
// it's the genesis block in which case use the current time.
var ts time.Time
switch {
case !blockTime.IsZero():
ts = blockTime
default:
ts = prevBlockTime.Add(time.Second)
}
extraNonce := uint64(0)
coinbaseScript, err := standardCoinbaseScript(blockHeight, extraNonce)
if err != nil {
return nil, err
}
coinbaseTx, err := createCoinbaseTx(coinbaseScript, blockHeight,
miningAddr, mineTo, net)
if err != nil {
return nil, err
}
// Create a new block ready to be solved.
blockTxns := []*btcutil.Tx{coinbaseTx}
if inclusionTxs != nil {
blockTxns = append(blockTxns, inclusionTxs...)
}
merkles := blockchain.BuildMerkleTreeStore(blockTxns, false)
var block wire.MsgBlock
block.Header = wire.BlockHeader{
Version: blockVersion,
PrevBlock: *prevHash,
MerkleRoot: *merkles[len(merkles)-1],
Timestamp: ts,
Bits: net.PowLimitBits,
}
for _, tx := range blockTxns {
if err := block.AddTransaction(tx.MsgTx()); err != nil {
return nil, err
}
}
found := solveBlock(&block.Header, net.PowLimit)
if !found {
return nil, errors.New("Unable to solve block")
}
utilBlock := btcutil.NewBlock(&block)
utilBlock.SetHeight(blockHeight)
return utilBlock, nil
}

View File

@ -0,0 +1,62 @@
// 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 rpctest
import (
"fmt"
"os/exec"
"path/filepath"
"runtime"
"sync"
)
var (
// compileMtx guards access to the executable path so that the project is
// only compiled once.
compileMtx sync.Mutex
// executablePath is the path to the compiled executable. This is the empty
// string until btcd is compiled. This should not be accessed directly;
// instead use the function btcdExecutablePath().
executablePath string
)
// btcdExecutablePath returns a path to the btcd executable to be used by
// rpctests. To ensure the code tests against the most up-to-date version of
// btcd, this method compiles btcd the first time it is called. After that, the
// generated binary is used for subsequent test harnesses. The executable file
// is not cleaned up, but since it lives at a static path in a temp directory,
// it is not a big deal.
func btcdExecutablePath() (string, error) {
compileMtx.Lock()
defer compileMtx.Unlock()
// If btcd has already been compiled, just use that.
if len(executablePath) != 0 {
return executablePath, nil
}
testDir, err := baseDir()
if err != nil {
return "", err
}
// Build btcd and output an executable in a static temp path.
outputPath := filepath.Join(testDir, "btcd")
if runtime.GOOS == "windows" {
outputPath += ".exe"
}
cmd := exec.Command(
"go", "build", "-o", outputPath, "github.com/btcsuite/btcd",
)
err = cmd.Run()
if err != nil {
return "", fmt.Errorf("Failed to build btcd: %v", err)
}
// Save executable path so future calls do not recompile.
executablePath = outputPath
return executablePath, nil
}

View File

@ -0,0 +1,12 @@
// Package rpctest provides a btcd-specific RPC testing harness crafting and
// executing integration tests by driving a `btcd` instance via the `RPC`
// interface. Each instance of an active harness comes equipped with a simple
// in-memory HD wallet capable of properly syncing to the generated chain,
// creating new addresses, and crafting fully signed transactions paying to an
// arbitrary set of outputs.
//
// This package was designed specifically to act as an RPC testing harness for
// `btcd`. However, the constructs presented are general enough to be adapted to
// any project wishing to programmatically drive a `btcd` instance of its
// systems/integration tests.
package rpctest

View File

@ -0,0 +1,591 @@
// 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 rpctest
import (
"bytes"
"encoding/binary"
"fmt"
"sync"
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
)
var (
// hdSeed is the BIP 32 seed used by the memWallet to initialize it's
// HD root key. This value is hard coded in order to ensure
// deterministic behavior across test runs.
hdSeed = [chainhash.HashSize]byte{
0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1,
0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8,
0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f,
0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
)
// utxo represents an unspent output spendable by the memWallet. The maturity
// height of the transaction is recorded in order to properly observe the
// maturity period of direct coinbase outputs.
type utxo struct {
pkScript []byte
value btcutil.Amount
keyIndex uint32
maturityHeight int32
isLocked bool
}
// isMature returns true if the target utxo is considered "mature" at the
// passed block height. Otherwise, false is returned.
func (u *utxo) isMature(height int32) bool {
return height >= u.maturityHeight
}
// chainUpdate encapsulates an update to the current main chain. This struct is
// used to sync up the memWallet each time a new block is connected to the main
// chain.
type chainUpdate struct {
blockHeight int32
filteredTxns []*btcutil.Tx
isConnect bool // True if connect, false if disconnect
}
// undoEntry is functionally the opposite of a chainUpdate. An undoEntry is
// created for each new block received, then stored in a log in order to
// properly handle block re-orgs.
type undoEntry struct {
utxosDestroyed map[wire.OutPoint]*utxo
utxosCreated []wire.OutPoint
}
// memWallet is a simple in-memory wallet whose purpose is to provide basic
// wallet functionality to the harness. The wallet uses a hard-coded HD key
// hierarchy which promotes reproducibility between harness test runs.
type memWallet struct {
coinbaseKey *btcec.PrivateKey
coinbaseAddr btcutil.Address
// hdRoot is the root master private key for the wallet.
hdRoot *hdkeychain.ExtendedKey
// hdIndex is the next available key index offset from the hdRoot.
hdIndex uint32
// currentHeight is the latest height the wallet is known to be synced
// to.
currentHeight int32
// addrs tracks all addresses belonging to the wallet. The addresses
// are indexed by their keypath from the hdRoot.
addrs map[uint32]btcutil.Address
// utxos is the set of utxos spendable by the wallet.
utxos map[wire.OutPoint]*utxo
// reorgJournal is a map storing an undo entry for each new block
// received. Once a block is disconnected, the undo entry for the
// particular height is evaluated, thereby rewinding the effect of the
// disconnected block on the wallet's set of spendable utxos.
reorgJournal map[int32]*undoEntry
chainUpdates []*chainUpdate
chainUpdateSignal chan struct{}
chainMtx sync.Mutex
net *chaincfg.Params
rpc *rpcclient.Client
sync.RWMutex
}
// newMemWallet creates and returns a fully initialized instance of the
// memWallet given a particular blockchain's parameters.
func newMemWallet(net *chaincfg.Params, harnessID uint32) (*memWallet, error) {
// The wallet's final HD seed is: hdSeed || harnessID. This method
// ensures that each harness instance uses a deterministic root seed
// based on its harness ID.
var harnessHDSeed [chainhash.HashSize + 4]byte
copy(harnessHDSeed[:], hdSeed[:])
binary.BigEndian.PutUint32(harnessHDSeed[:chainhash.HashSize], harnessID)
hdRoot, err := hdkeychain.NewMaster(harnessHDSeed[:], net)
if err != nil {
return nil, nil
}
// The first child key from the hd root is reserved as the coinbase
// generation address.
coinbaseChild, err := hdRoot.Child(0)
if err != nil {
return nil, err
}
coinbaseKey, err := coinbaseChild.ECPrivKey()
if err != nil {
return nil, err
}
coinbaseAddr, err := keyToAddr(coinbaseKey, net)
if err != nil {
return nil, err
}
// Track the coinbase generation address to ensure we properly track
// newly generated bitcoin we can spend.
addrs := make(map[uint32]btcutil.Address)
addrs[0] = coinbaseAddr
return &memWallet{
net: net,
coinbaseKey: coinbaseKey,
coinbaseAddr: coinbaseAddr,
hdIndex: 1,
hdRoot: hdRoot,
addrs: addrs,
utxos: make(map[wire.OutPoint]*utxo),
chainUpdateSignal: make(chan struct{}),
reorgJournal: make(map[int32]*undoEntry),
}, nil
}
// Start launches all goroutines required for the wallet to function properly.
func (m *memWallet) Start() {
go m.chainSyncer()
}
// SyncedHeight returns the height the wallet is known to be synced to.
//
// This function is safe for concurrent access.
func (m *memWallet) SyncedHeight() int32 {
m.RLock()
defer m.RUnlock()
return m.currentHeight
}
// SetRPCClient saves the passed rpc connection to btcd as the wallet's
// personal rpc connection.
func (m *memWallet) SetRPCClient(rpcClient *rpcclient.Client) {
m.rpc = rpcClient
}
// IngestBlock is a call-back which is to be triggered each time a new block is
// connected to the main chain. It queues the update for the chain syncer,
// calling the private version in sequential order.
func (m *memWallet) IngestBlock(height int32, header *wire.BlockHeader, filteredTxns []*btcutil.Tx) {
// Append this new chain update to the end of the queue of new chain
// updates.
m.chainMtx.Lock()
m.chainUpdates = append(m.chainUpdates, &chainUpdate{height,
filteredTxns, true})
m.chainMtx.Unlock()
// Launch a goroutine to signal the chainSyncer that a new update is
// available. We do this in a new goroutine in order to avoid blocking
// the main loop of the rpc client.
go func() {
m.chainUpdateSignal <- struct{}{}
}()
}
// ingestBlock updates the wallet's internal utxo state based on the outputs
// created and destroyed within each block.
func (m *memWallet) ingestBlock(update *chainUpdate) {
// Update the latest synced height, then process each filtered
// transaction in the block creating and destroying utxos within
// the wallet as a result.
m.currentHeight = update.blockHeight
undo := &undoEntry{
utxosDestroyed: make(map[wire.OutPoint]*utxo),
}
for _, tx := range update.filteredTxns {
mtx := tx.MsgTx()
isCoinbase := blockchain.IsCoinBaseTx(mtx)
txHash := mtx.TxHash()
m.evalOutputs(mtx.TxOut, &txHash, isCoinbase, undo)
m.evalInputs(mtx.TxIn, undo)
}
// Finally, record the undo entry for this block so we can
// properly update our internal state in response to the block
// being re-org'd from the main chain.
m.reorgJournal[update.blockHeight] = undo
}
// chainSyncer is a goroutine dedicated to processing new blocks in order to
// keep the wallet's utxo state up to date.
//
// NOTE: This MUST be run as a goroutine.
func (m *memWallet) chainSyncer() {
var update *chainUpdate
for range m.chainUpdateSignal {
// A new update is available, so pop the new chain update from
// the front of the update queue.
m.chainMtx.Lock()
update = m.chainUpdates[0]
m.chainUpdates[0] = nil // Set to nil to prevent GC leak.
m.chainUpdates = m.chainUpdates[1:]
m.chainMtx.Unlock()
m.Lock()
if update.isConnect {
m.ingestBlock(update)
} else {
m.unwindBlock(update)
}
m.Unlock()
}
}
// evalOutputs evaluates each of the passed outputs, creating a new matching
// utxo within the wallet if we're able to spend the output.
func (m *memWallet) evalOutputs(outputs []*wire.TxOut, txHash *chainhash.Hash,
isCoinbase bool, undo *undoEntry) {
for i, output := range outputs {
pkScript := output.PkScript
// Scan all the addresses we currently control to see if the
// output is paying to us.
for keyIndex, addr := range m.addrs {
pkHash := addr.ScriptAddress()
if !bytes.Contains(pkScript, pkHash) {
continue
}
// If this is a coinbase output, then we mark the
// maturity height at the proper block height in the
// future.
var maturityHeight int32
if isCoinbase {
maturityHeight = m.currentHeight + int32(m.net.CoinbaseMaturity)
}
op := wire.OutPoint{Hash: *txHash, Index: uint32(i)}
m.utxos[op] = &utxo{
value: btcutil.Amount(output.Value),
keyIndex: keyIndex,
maturityHeight: maturityHeight,
pkScript: pkScript,
}
undo.utxosCreated = append(undo.utxosCreated, op)
}
}
}
// evalInputs scans all the passed inputs, destroying any utxos within the
// wallet which are spent by an input.
func (m *memWallet) evalInputs(inputs []*wire.TxIn, undo *undoEntry) {
for _, txIn := range inputs {
op := txIn.PreviousOutPoint
oldUtxo, ok := m.utxos[op]
if !ok {
continue
}
undo.utxosDestroyed[op] = oldUtxo
delete(m.utxos, op)
}
}
// UnwindBlock is a call-back which is to be executed each time a block is
// disconnected from the main chain. It queues the update for the chain syncer,
// calling the private version in sequential order.
func (m *memWallet) UnwindBlock(height int32, header *wire.BlockHeader) {
// Append this new chain update to the end of the queue of new chain
// updates.
m.chainMtx.Lock()
m.chainUpdates = append(m.chainUpdates, &chainUpdate{height,
nil, false})
m.chainMtx.Unlock()
// Launch a goroutine to signal the chainSyncer that a new update is
// available. We do this in a new goroutine in order to avoid blocking
// the main loop of the rpc client.
go func() {
m.chainUpdateSignal <- struct{}{}
}()
}
// unwindBlock undoes the effect that a particular block had on the wallet's
// internal utxo state.
func (m *memWallet) unwindBlock(update *chainUpdate) {
undo := m.reorgJournal[update.blockHeight]
for _, utxo := range undo.utxosCreated {
delete(m.utxos, utxo)
}
for outPoint, utxo := range undo.utxosDestroyed {
m.utxos[outPoint] = utxo
}
delete(m.reorgJournal, update.blockHeight)
}
// newAddress returns a new address from the wallet's hd key chain. It also
// loads the address into the RPC client's transaction filter to ensure any
// transactions that involve it are delivered via the notifications.
func (m *memWallet) newAddress() (btcutil.Address, error) {
index := m.hdIndex
childKey, err := m.hdRoot.Child(index)
if err != nil {
return nil, err
}
privKey, err := childKey.ECPrivKey()
if err != nil {
return nil, err
}
addr, err := keyToAddr(privKey, m.net)
if err != nil {
return nil, err
}
err = m.rpc.LoadTxFilter(false, []btcutil.Address{addr}, nil)
if err != nil {
return nil, err
}
m.addrs[index] = addr
m.hdIndex++
return addr, nil
}
// NewAddress returns a fresh address spendable by the wallet.
//
// This function is safe for concurrent access.
func (m *memWallet) NewAddress() (btcutil.Address, error) {
m.Lock()
defer m.Unlock()
return m.newAddress()
}
// fundTx attempts to fund a transaction sending amt bitcoin. The coins are
// selected such that the final amount spent pays enough fees as dictated by the
// passed fee rate. The passed fee rate should be expressed in
// satoshis-per-byte. The transaction being funded can optionally include a
// change output indicated by the change boolean.
//
// NOTE: The memWallet's mutex must be held when this function is called.
func (m *memWallet) fundTx(tx *wire.MsgTx, amt btcutil.Amount,
feeRate btcutil.Amount, change bool) error {
const (
// spendSize is the largest number of bytes of a sigScript
// which spends a p2pkh output: OP_DATA_73 <sig> OP_DATA_33 <pubkey>
spendSize = 1 + 73 + 1 + 33
)
var (
amtSelected btcutil.Amount
txSize int
)
for outPoint, utxo := range m.utxos {
// Skip any outputs that are still currently immature or are
// currently locked.
if !utxo.isMature(m.currentHeight) || utxo.isLocked {
continue
}
amtSelected += utxo.value
// Add the selected output to the transaction, updating the
// current tx size while accounting for the size of the future
// sigScript.
tx.AddTxIn(wire.NewTxIn(&outPoint, nil, nil))
txSize = tx.SerializeSize() + spendSize*len(tx.TxIn)
// Calculate the fee required for the txn at this point
// observing the specified fee rate. If we don't have enough
// coins from he current amount selected to pay the fee, then
// continue to grab more coins.
reqFee := btcutil.Amount(txSize * int(feeRate))
if amtSelected-reqFee < amt {
continue
}
// If we have any change left over and we should create a change
// output, then add an additional output to the transaction
// reserved for it.
changeVal := amtSelected - amt - reqFee
if changeVal > 0 && change {
addr, err := m.newAddress()
if err != nil {
return err
}
pkScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return err
}
changeOutput := &wire.TxOut{
Value: int64(changeVal),
PkScript: pkScript,
}
tx.AddTxOut(changeOutput)
}
return nil
}
// If we've reached this point, then coin selection failed due to an
// insufficient amount of coins.
return fmt.Errorf("not enough funds for coin selection")
}
// SendOutputs creates, then sends a transaction paying to the specified output
// while observing the passed fee rate. The passed fee rate should be expressed
// in satoshis-per-byte.
func (m *memWallet) SendOutputs(outputs []*wire.TxOut,
feeRate btcutil.Amount) (*chainhash.Hash, error) {
tx, err := m.CreateTransaction(outputs, feeRate, true)
if err != nil {
return nil, err
}
return m.rpc.SendRawTransaction(tx, true)
}
// SendOutputsWithoutChange creates and sends a transaction that pays to the
// specified outputs while observing the passed fee rate and ignoring a change
// output. The passed fee rate should be expressed in sat/b.
func (m *memWallet) SendOutputsWithoutChange(outputs []*wire.TxOut,
feeRate btcutil.Amount) (*chainhash.Hash, error) {
tx, err := m.CreateTransaction(outputs, feeRate, false)
if err != nil {
return nil, err
}
return m.rpc.SendRawTransaction(tx, true)
}
// CreateTransaction returns a fully signed transaction paying to the specified
// outputs while observing the desired fee rate. The passed fee rate should be
// expressed in satoshis-per-byte. The transaction being created can optionally
// include a change output indicated by the change boolean.
//
// This function is safe for concurrent access.
func (m *memWallet) CreateTransaction(outputs []*wire.TxOut,
feeRate btcutil.Amount, change bool) (*wire.MsgTx, error) {
m.Lock()
defer m.Unlock()
tx := wire.NewMsgTx(wire.TxVersion)
// Tally up the total amount to be sent in order to perform coin
// selection shortly below.
var outputAmt btcutil.Amount
for _, output := range outputs {
outputAmt += btcutil.Amount(output.Value)
tx.AddTxOut(output)
}
// Attempt to fund the transaction with spendable utxos.
if err := m.fundTx(tx, outputAmt, feeRate, change); err != nil {
return nil, err
}
// Populate all the selected inputs with valid sigScript for spending.
// Along the way record all outputs being spent in order to avoid a
// potential double spend.
spentOutputs := make([]*utxo, 0, len(tx.TxIn))
for i, txIn := range tx.TxIn {
outPoint := txIn.PreviousOutPoint
utxo := m.utxos[outPoint]
extendedKey, err := m.hdRoot.Child(utxo.keyIndex)
if err != nil {
return nil, err
}
privKey, err := extendedKey.ECPrivKey()
if err != nil {
return nil, err
}
sigScript, err := txscript.SignatureScript(tx, i, utxo.pkScript,
txscript.SigHashAll, privKey, true)
if err != nil {
return nil, err
}
txIn.SignatureScript = sigScript
spentOutputs = append(spentOutputs, utxo)
}
// As these outputs are now being spent by this newly created
// transaction, mark the outputs are "locked". This action ensures
// these outputs won't be double spent by any subsequent transactions.
// These locked outputs can be freed via a call to UnlockOutputs.
for _, utxo := range spentOutputs {
utxo.isLocked = true
}
return tx, nil
}
// UnlockOutputs unlocks any outputs which were previously locked due to
// being selected to fund a transaction via the CreateTransaction method.
//
// This function is safe for concurrent access.
func (m *memWallet) UnlockOutputs(inputs []*wire.TxIn) {
m.Lock()
defer m.Unlock()
for _, input := range inputs {
utxo, ok := m.utxos[input.PreviousOutPoint]
if !ok {
continue
}
utxo.isLocked = false
}
}
// ConfirmedBalance returns the confirmed balance of the wallet.
//
// This function is safe for concurrent access.
func (m *memWallet) ConfirmedBalance() btcutil.Amount {
m.RLock()
defer m.RUnlock()
var balance btcutil.Amount
for _, utxo := range m.utxos {
// Prevent any immature or locked outputs from contributing to
// the wallet's total confirmed balance.
if !utxo.isMature(m.currentHeight) || utxo.isLocked {
continue
}
balance += utxo.value
}
return balance
}
// keyToAddr maps the passed private to corresponding p2pkh address.
func keyToAddr(key *btcec.PrivateKey, net *chaincfg.Params) (btcutil.Address, error) {
serializedKey := key.PubKey().SerializeCompressed()
pubKeyAddr, err := btcutil.NewAddressPubKey(serializedKey, net)
if err != nil {
return nil, err
}
return pubKeyAddr.AddressPubKeyHash(), nil
}

View File

@ -0,0 +1,287 @@
// 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 rpctest
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"time"
rpc "github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcutil"
)
// nodeConfig contains all the args, and data required to launch a btcd process
// and connect the rpc client to it.
type nodeConfig struct {
rpcUser string
rpcPass string
listen string
rpcListen string
rpcConnect string
dataDir string
logDir string
profile string
debugLevel string
extra []string
prefix string
exe string
endpoint string
certFile string
keyFile string
certificates []byte
}
// newConfig returns a newConfig with all default values.
func newConfig(prefix, certFile, keyFile string, extra []string) (*nodeConfig, error) {
btcdPath, err := btcdExecutablePath()
if err != nil {
btcdPath = "btcd"
}
a := &nodeConfig{
listen: "127.0.0.1:18555",
rpcListen: "127.0.0.1:18556",
rpcUser: "user",
rpcPass: "pass",
extra: extra,
prefix: prefix,
exe: btcdPath,
endpoint: "ws",
certFile: certFile,
keyFile: keyFile,
}
if err := a.setDefaults(); err != nil {
return nil, err
}
return a, nil
}
// setDefaults sets the default values of the config. It also creates the
// temporary data, and log directories which must be cleaned up with a call to
// cleanup().
func (n *nodeConfig) setDefaults() error {
datadir, err := ioutil.TempDir("", n.prefix+"-data")
if err != nil {
return err
}
n.dataDir = datadir
logdir, err := ioutil.TempDir("", n.prefix+"-logs")
if err != nil {
return err
}
n.logDir = logdir
cert, err := ioutil.ReadFile(n.certFile)
if err != nil {
return err
}
n.certificates = cert
return nil
}
// arguments returns an array of arguments that be used to launch the btcd
// process.
func (n *nodeConfig) arguments() []string {
args := []string{}
if n.rpcUser != "" {
// --rpcuser
args = append(args, fmt.Sprintf("--rpcuser=%s", n.rpcUser))
}
if n.rpcPass != "" {
// --rpcpass
args = append(args, fmt.Sprintf("--rpcpass=%s", n.rpcPass))
}
if n.listen != "" {
// --listen
args = append(args, fmt.Sprintf("--listen=%s", n.listen))
}
if n.rpcListen != "" {
// --rpclisten
args = append(args, fmt.Sprintf("--rpclisten=%s", n.rpcListen))
}
if n.rpcConnect != "" {
// --rpcconnect
args = append(args, fmt.Sprintf("--rpcconnect=%s", n.rpcConnect))
}
// --rpccert
args = append(args, fmt.Sprintf("--rpccert=%s", n.certFile))
// --rpckey
args = append(args, fmt.Sprintf("--rpckey=%s", n.keyFile))
if n.dataDir != "" {
// --datadir
args = append(args, fmt.Sprintf("--datadir=%s", n.dataDir))
}
if n.logDir != "" {
// --logdir
args = append(args, fmt.Sprintf("--logdir=%s", n.logDir))
}
if n.profile != "" {
// --profile
args = append(args, fmt.Sprintf("--profile=%s", n.profile))
}
if n.debugLevel != "" {
// --debuglevel
args = append(args, fmt.Sprintf("--debuglevel=%s", n.debugLevel))
}
args = append(args, n.extra...)
return args
}
// command returns the exec.Cmd which will be used to start the btcd process.
func (n *nodeConfig) command() *exec.Cmd {
return exec.Command(n.exe, n.arguments()...)
}
// rpcConnConfig returns the rpc connection config that can be used to connect
// to the btcd process that is launched via Start().
func (n *nodeConfig) rpcConnConfig() rpc.ConnConfig {
return rpc.ConnConfig{
Host: n.rpcListen,
Endpoint: n.endpoint,
User: n.rpcUser,
Pass: n.rpcPass,
Certificates: n.certificates,
DisableAutoReconnect: true,
}
}
// String returns the string representation of this nodeConfig.
func (n *nodeConfig) String() string {
return n.prefix
}
// cleanup removes the tmp data and log directories.
func (n *nodeConfig) cleanup() error {
dirs := []string{
n.logDir,
n.dataDir,
}
var err error
for _, dir := range dirs {
if err = os.RemoveAll(dir); err != nil {
log.Printf("Cannot remove dir %s: %v", dir, err)
}
}
return err
}
// node houses the necessary state required to configure, launch, and manage a
// btcd process.
type node struct {
config *nodeConfig
cmd *exec.Cmd
pidFile string
dataDir string
}
// newNode creates a new node instance according to the passed config. dataDir
// will be used to hold a file recording the pid of the launched process, and
// as the base for the log and data directories for btcd.
func newNode(config *nodeConfig, dataDir string) (*node, error) {
return &node{
config: config,
dataDir: dataDir,
cmd: config.command(),
}, nil
}
// start creates a new btcd process, and writes its pid in a file reserved for
// recording the pid of the launched process. This file can be used to
// terminate the process in case of a hang, or panic. In the case of a failing
// test case, or panic, it is important that the process be stopped via stop(),
// otherwise, it will persist unless explicitly killed.
func (n *node) start() error {
if err := n.cmd.Start(); err != nil {
return err
}
pid, err := os.Create(filepath.Join(n.dataDir,
fmt.Sprintf("%s.pid", n.config)))
if err != nil {
return err
}
n.pidFile = pid.Name()
if _, err = fmt.Fprintf(pid, "%d\n", n.cmd.Process.Pid); err != nil {
return err
}
if err := pid.Close(); err != nil {
return err
}
return nil
}
// stop interrupts the running btcd process process, and waits until it exits
// properly. On windows, interrupt is not supported, so a kill signal is used
// instead
func (n *node) stop() error {
if n.cmd == nil || n.cmd.Process == nil {
// return if not properly initialized
// or error starting the process
return nil
}
defer n.cmd.Wait()
if runtime.GOOS == "windows" {
return n.cmd.Process.Signal(os.Kill)
}
return n.cmd.Process.Signal(os.Interrupt)
}
// cleanup cleanups process and args files. The file housing the pid of the
// created process will be deleted, as well as any directories created by the
// process.
func (n *node) cleanup() error {
if n.pidFile != "" {
if err := os.Remove(n.pidFile); err != nil {
log.Printf("unable to remove file %s: %v", n.pidFile,
err)
}
}
return n.config.cleanup()
}
// shutdown terminates the running btcd process, and cleans up all
// file/directories created by node.
func (n *node) shutdown() error {
if err := n.stop(); err != nil {
return err
}
if err := n.cleanup(); err != nil {
return err
}
return nil
}
// genCertPair generates a key/cert pair to the paths provided.
func genCertPair(certFile, keyFile string) error {
org := "rpctest autogenerated cert"
validUntil := time.Now().Add(10 * 365 * 24 * time.Hour)
cert, key, err := btcutil.NewTLSCertPair(org, validUntil, nil)
if err != nil {
return err
}
// Write cert and key files.
if err = ioutil.WriteFile(certFile, cert, 0666); err != nil {
return err
}
if err = ioutil.WriteFile(keyFile, key, 0600); err != nil {
os.Remove(certFile)
return err
}
return nil
}

View File

@ -0,0 +1,500 @@
// 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 rpctest
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"strconv"
"sync"
"testing"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
const (
// These constants define the minimum and maximum p2p and rpc port
// numbers used by a test harness. The min port is inclusive while the
// max port is exclusive.
minPeerPort = 10000
maxPeerPort = 35000
minRPCPort = maxPeerPort
maxRPCPort = 60000
// BlockVersion is the default block version used when generating
// blocks.
BlockVersion = 4
)
var (
// current number of active test nodes.
numTestInstances = 0
// processID is the process ID of the current running process. It is
// used to calculate ports based upon it when launching an rpc
// harnesses. The intent is to allow multiple process to run in
// parallel without port collisions.
//
// It should be noted however that there is still some small probability
// that there will be port collisions either due to other processes
// running or simply due to the stars aligning on the process IDs.
processID = os.Getpid()
// testInstances is a private package-level slice used to keep track of
// all active test harnesses. This global can be used to perform
// various "joins", shutdown several active harnesses after a test,
// etc.
testInstances = make(map[string]*Harness)
// Used to protest concurrent access to above declared variables.
harnessStateMtx sync.RWMutex
)
// HarnessTestCase represents a test-case which utilizes an instance of the
// Harness to exercise functionality.
type HarnessTestCase func(r *Harness, t *testing.T)
// Harness fully encapsulates an active btcd process to provide a unified
// platform for creating rpc driven integration tests involving btcd. The
// active btcd node will typically be run in simnet mode in order to allow for
// easy generation of test blockchains. The active btcd process is fully
// managed by Harness, which handles the necessary initialization, and teardown
// of the process along with any temporary directories created as a result.
// Multiple Harness instances may be run concurrently, in order to allow for
// testing complex scenarios involving multiple nodes. The harness also
// includes an in-memory wallet to streamline various classes of tests.
type Harness struct {
// ActiveNet is the parameters of the blockchain the Harness belongs
// to.
ActiveNet *chaincfg.Params
Node *rpcclient.Client
node *node
handlers *rpcclient.NotificationHandlers
wallet *memWallet
testNodeDir string
maxConnRetries int
nodeNum int
sync.Mutex
}
// New creates and initializes new instance of the rpc test harness.
// Optionally, websocket handlers and a specified configuration may be passed.
// In the case that a nil config is passed, a default configuration will be
// used.
//
// NOTE: This function is safe for concurrent access.
func New(activeNet *chaincfg.Params, handlers *rpcclient.NotificationHandlers,
extraArgs []string) (*Harness, error) {
harnessStateMtx.Lock()
defer harnessStateMtx.Unlock()
// Add a flag for the appropriate network type based on the provided
// chain params.
switch activeNet.Net {
case wire.MainNet:
// No extra flags since mainnet is the default
case wire.TestNet3:
extraArgs = append(extraArgs, "--testnet")
case wire.TestNet:
extraArgs = append(extraArgs, "--regtest")
case wire.SimNet:
extraArgs = append(extraArgs, "--simnet")
default:
return nil, fmt.Errorf("rpctest.New must be called with one " +
"of the supported chain networks")
}
testDir, err := baseDir()
if err != nil {
return nil, err
}
harnessID := strconv.Itoa(numTestInstances)
nodeTestData, err := ioutil.TempDir(testDir, "harness-"+harnessID)
if err != nil {
return nil, err
}
certFile := filepath.Join(nodeTestData, "rpc.cert")
keyFile := filepath.Join(nodeTestData, "rpc.key")
if err := genCertPair(certFile, keyFile); err != nil {
return nil, err
}
wallet, err := newMemWallet(activeNet, uint32(numTestInstances))
if err != nil {
return nil, err
}
miningAddr := fmt.Sprintf("--miningaddr=%s", wallet.coinbaseAddr)
extraArgs = append(extraArgs, miningAddr)
config, err := newConfig("rpctest", certFile, keyFile, extraArgs)
if err != nil {
return nil, err
}
// Generate p2p+rpc listening addresses.
config.listen, config.rpcListen = generateListeningAddresses()
// Create the testing node bounded to the simnet.
node, err := newNode(config, nodeTestData)
if err != nil {
return nil, err
}
nodeNum := numTestInstances
numTestInstances++
if handlers == nil {
handlers = &rpcclient.NotificationHandlers{}
}
// If a handler for the OnFilteredBlock{Connected,Disconnected} callback
// callback has already been set, then create a wrapper callback which
// executes both the currently registered callback and the mem wallet's
// callback.
if handlers.OnFilteredBlockConnected != nil {
obc := handlers.OnFilteredBlockConnected
handlers.OnFilteredBlockConnected = func(height int32, header *wire.BlockHeader, filteredTxns []*btcutil.Tx) {
wallet.IngestBlock(height, header, filteredTxns)
obc(height, header, filteredTxns)
}
} else {
// Otherwise, we can claim the callback ourselves.
handlers.OnFilteredBlockConnected = wallet.IngestBlock
}
if handlers.OnFilteredBlockDisconnected != nil {
obd := handlers.OnFilteredBlockDisconnected
handlers.OnFilteredBlockDisconnected = func(height int32, header *wire.BlockHeader) {
wallet.UnwindBlock(height, header)
obd(height, header)
}
} else {
handlers.OnFilteredBlockDisconnected = wallet.UnwindBlock
}
h := &Harness{
handlers: handlers,
node: node,
maxConnRetries: 20,
testNodeDir: nodeTestData,
ActiveNet: activeNet,
nodeNum: nodeNum,
wallet: wallet,
}
// Track this newly created test instance within the package level
// global map of all active test instances.
testInstances[h.testNodeDir] = h
return h, nil
}
// SetUp initializes the rpc test state. Initialization includes: starting up a
// simnet node, creating a websockets client and connecting to the started
// node, and finally: optionally generating and submitting a testchain with a
// configurable number of mature coinbase outputs coinbase outputs.
//
// NOTE: This method and TearDown should always be called from the same
// goroutine as they are not concurrent safe.
func (h *Harness) SetUp(createTestChain bool, numMatureOutputs uint32) error {
// Start the btcd node itself. This spawns a new process which will be
// managed
if err := h.node.start(); err != nil {
return err
}
if err := h.connectRPCClient(); err != nil {
return err
}
h.wallet.Start()
// Filter transactions that pay to the coinbase associated with the
// wallet.
filterAddrs := []btcutil.Address{h.wallet.coinbaseAddr}
if err := h.Node.LoadTxFilter(true, filterAddrs, nil); err != nil {
return err
}
// Ensure btcd properly dispatches our registered call-back for each new
// block. Otherwise, the memWallet won't function properly.
if err := h.Node.NotifyBlocks(); err != nil {
return err
}
// Create a test chain with the desired number of mature coinbase
// outputs.
if createTestChain && numMatureOutputs != 0 {
numToGenerate := (uint32(h.ActiveNet.CoinbaseMaturity) +
numMatureOutputs)
_, err := h.Node.Generate(numToGenerate)
if err != nil {
return err
}
}
// Block until the wallet has fully synced up to the tip of the main
// chain.
_, height, err := h.Node.GetBestBlock()
if err != nil {
return err
}
ticker := time.NewTicker(time.Millisecond * 100)
for range ticker.C {
walletHeight := h.wallet.SyncedHeight()
if walletHeight == height {
break
}
}
ticker.Stop()
return nil
}
// tearDown stops the running rpc test instance. All created processes are
// killed, and temporary directories removed.
//
// This function MUST be called with the harness state mutex held (for writes).
func (h *Harness) tearDown() error {
if h.Node != nil {
h.Node.Shutdown()
}
if err := h.node.shutdown(); err != nil {
return err
}
if err := os.RemoveAll(h.testNodeDir); err != nil {
return err
}
delete(testInstances, h.testNodeDir)
return nil
}
// TearDown stops the running rpc test instance. All created processes are
// killed, and temporary directories removed.
//
// NOTE: This method and SetUp should always be called from the same goroutine
// as they are not concurrent safe.
func (h *Harness) TearDown() error {
harnessStateMtx.Lock()
defer harnessStateMtx.Unlock()
return h.tearDown()
}
// connectRPCClient attempts to establish an RPC connection to the created btcd
// process belonging to this Harness instance. If the initial connection
// attempt fails, this function will retry h.maxConnRetries times, backing off
// the time between subsequent attempts. If after h.maxConnRetries attempts,
// we're not able to establish a connection, this function returns with an
// error.
func (h *Harness) connectRPCClient() error {
var client *rpcclient.Client
var err error
rpcConf := h.node.config.rpcConnConfig()
for i := 0; i < h.maxConnRetries; i++ {
if client, err = rpcclient.New(&rpcConf, h.handlers); err != nil {
time.Sleep(time.Duration(i) * 50 * time.Millisecond)
continue
}
break
}
if client == nil {
return fmt.Errorf("connection timeout")
}
h.Node = client
h.wallet.SetRPCClient(client)
return nil
}
// NewAddress returns a fresh address spendable by the Harness' internal
// wallet.
//
// This function is safe for concurrent access.
func (h *Harness) NewAddress() (btcutil.Address, error) {
return h.wallet.NewAddress()
}
// ConfirmedBalance returns the confirmed balance of the Harness' internal
// wallet.
//
// This function is safe for concurrent access.
func (h *Harness) ConfirmedBalance() btcutil.Amount {
return h.wallet.ConfirmedBalance()
}
// SendOutputs creates, signs, and finally broadcasts a transaction spending
// the harness' available mature coinbase outputs creating new outputs
// according to targetOutputs.
//
// This function is safe for concurrent access.
func (h *Harness) SendOutputs(targetOutputs []*wire.TxOut,
feeRate btcutil.Amount) (*chainhash.Hash, error) {
return h.wallet.SendOutputs(targetOutputs, feeRate)
}
// SendOutputsWithoutChange creates and sends a transaction that pays to the
// specified outputs while observing the passed fee rate and ignoring a change
// output. The passed fee rate should be expressed in sat/b.
//
// This function is safe for concurrent access.
func (h *Harness) SendOutputsWithoutChange(targetOutputs []*wire.TxOut,
feeRate btcutil.Amount) (*chainhash.Hash, error) {
return h.wallet.SendOutputsWithoutChange(targetOutputs, feeRate)
}
// CreateTransaction returns a fully signed transaction paying to the specified
// outputs while observing the desired fee rate. The passed fee rate should be
// expressed in satoshis-per-byte. The transaction being created can optionally
// include a change output indicated by the change boolean. Any unspent outputs
// selected as inputs for the crafted transaction are marked as unspendable in
// order to avoid potential double-spends by future calls to this method. If the
// created transaction is cancelled for any reason then the selected inputs MUST
// be freed via a call to UnlockOutputs. Otherwise, the locked inputs won't be
// returned to the pool of spendable outputs.
//
// This function is safe for concurrent access.
func (h *Harness) CreateTransaction(targetOutputs []*wire.TxOut,
feeRate btcutil.Amount, change bool) (*wire.MsgTx, error) {
return h.wallet.CreateTransaction(targetOutputs, feeRate, change)
}
// UnlockOutputs unlocks any outputs which were previously marked as
// unspendabe due to being selected to fund a transaction via the
// CreateTransaction method.
//
// This function is safe for concurrent access.
func (h *Harness) UnlockOutputs(inputs []*wire.TxIn) {
h.wallet.UnlockOutputs(inputs)
}
// RPCConfig returns the harnesses current rpc configuration. This allows other
// potential RPC clients created within tests to connect to a given test
// harness instance.
func (h *Harness) RPCConfig() rpcclient.ConnConfig {
return h.node.config.rpcConnConfig()
}
// P2PAddress returns the harness' P2P listening address. This allows potential
// peers (such as SPV peers) created within tests to connect to a given test
// harness instance.
func (h *Harness) P2PAddress() string {
return h.node.config.listen
}
// GenerateAndSubmitBlock creates a block whose contents include the passed
// transactions and submits it to the running simnet node. For generating
// blocks with only a coinbase tx, callers can simply pass nil instead of
// transactions to be mined. Additionally, a custom block version can be set by
// the caller. A blockVersion of -1 indicates that the current default block
// version should be used. An uninitialized time.Time should be used for the
// blockTime parameter if one doesn't wish to set a custom time.
//
// This function is safe for concurrent access.
func (h *Harness) GenerateAndSubmitBlock(txns []*btcutil.Tx, blockVersion int32,
blockTime time.Time) (*btcutil.Block, error) {
return h.GenerateAndSubmitBlockWithCustomCoinbaseOutputs(txns,
blockVersion, blockTime, []wire.TxOut{})
}
// GenerateAndSubmitBlockWithCustomCoinbaseOutputs creates a block whose
// contents include the passed coinbase outputs and transactions and submits
// it to the running simnet node. For generating blocks with only a coinbase tx,
// callers can simply pass nil instead of transactions to be mined.
// Additionally, a custom block version can be set by the caller. A blockVersion
// of -1 indicates that the current default block version should be used. An
// uninitialized time.Time should be used for the blockTime parameter if one
// doesn't wish to set a custom time. The mineTo list of outputs will be added
// to the coinbase; this is not checked for correctness until the block is
// submitted; thus, it is the caller's responsibility to ensure that the outputs
// are correct. If the list is empty, the coinbase reward goes to the wallet
// managed by the Harness.
//
// This function is safe for concurrent access.
func (h *Harness) GenerateAndSubmitBlockWithCustomCoinbaseOutputs(
txns []*btcutil.Tx, blockVersion int32, blockTime time.Time,
mineTo []wire.TxOut) (*btcutil.Block, error) {
h.Lock()
defer h.Unlock()
if blockVersion == -1 {
blockVersion = BlockVersion
}
prevBlockHash, prevBlockHeight, err := h.Node.GetBestBlock()
if err != nil {
return nil, err
}
mBlock, err := h.Node.GetBlock(prevBlockHash)
if err != nil {
return nil, err
}
prevBlock := btcutil.NewBlock(mBlock)
prevBlock.SetHeight(prevBlockHeight)
// Create a new block including the specified transactions
newBlock, err := CreateBlock(prevBlock, txns, blockVersion,
blockTime, h.wallet.coinbaseAddr, mineTo, h.ActiveNet)
if err != nil {
return nil, err
}
// Submit the block to the simnet node.
if err := h.Node.SubmitBlock(newBlock, nil); err != nil {
return nil, err
}
return newBlock, nil
}
// generateListeningAddresses returns two strings representing listening
// addresses designated for the current rpc test. If there haven't been any
// test instances created, the default ports are used. Otherwise, in order to
// support multiple test nodes running at once, the p2p and rpc port are
// incremented after each initialization.
func generateListeningAddresses() (string, string) {
localhost := "127.0.0.1"
portString := func(minPort, maxPort int) string {
port := minPort + numTestInstances + ((20 * processID) %
(maxPort - minPort))
return strconv.Itoa(port)
}
p2p := net.JoinHostPort(localhost, portString(minPeerPort, maxPeerPort))
rpc := net.JoinHostPort(localhost, portString(minRPCPort, maxRPCPort))
return p2p, rpc
}
// baseDir is the directory path of the temp directory for all rpctest files.
func baseDir() (string, error) {
dirPath := filepath.Join(os.TempDir(), "btcd", "rpctest")
err := os.MkdirAll(dirPath, 0755)
return dirPath, err
}

View File

@ -0,0 +1,164 @@
// 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 rpctest
import (
"reflect"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
)
// JoinType is an enum representing a particular type of "node join". A node
// join is a synchronization tool used to wait until a subset of nodes have a
// consistent state with respect to an attribute.
type JoinType uint8
const (
// Blocks is a JoinType which waits until all nodes share the same
// block height.
Blocks JoinType = iota
// Mempools is a JoinType which blocks until all nodes have identical
// mempool.
Mempools
)
// JoinNodes is a synchronization tool used to block until all passed nodes are
// fully synced with respect to an attribute. This function will block for a
// period of time, finally returning once all nodes are synced according to the
// passed JoinType. This function be used to to ensure all active test
// harnesses are at a consistent state before proceeding to an assertion or
// check within rpc tests.
func JoinNodes(nodes []*Harness, joinType JoinType) error {
switch joinType {
case Blocks:
return syncBlocks(nodes)
case Mempools:
return syncMempools(nodes)
}
return nil
}
// syncMempools blocks until all nodes have identical mempools.
func syncMempools(nodes []*Harness) error {
poolsMatch := false
retry:
for !poolsMatch {
firstPool, err := nodes[0].Node.GetRawMempool()
if err != nil {
return err
}
// If all nodes have an identical mempool with respect to the
// first node, then we're done. Otherwise, drop back to the top
// of the loop and retry after a short wait period.
for _, node := range nodes[1:] {
nodePool, err := node.Node.GetRawMempool()
if err != nil {
return err
}
if !reflect.DeepEqual(firstPool, nodePool) {
time.Sleep(time.Millisecond * 100)
continue retry
}
}
poolsMatch = true
}
return nil
}
// syncBlocks blocks until all nodes report the same best chain.
func syncBlocks(nodes []*Harness) error {
blocksMatch := false
retry:
for !blocksMatch {
var prevHash *chainhash.Hash
var prevHeight int32
for _, node := range nodes {
blockHash, blockHeight, err := node.Node.GetBestBlock()
if err != nil {
return err
}
if prevHash != nil && (*blockHash != *prevHash ||
blockHeight != prevHeight) {
time.Sleep(time.Millisecond * 100)
continue retry
}
prevHash, prevHeight = blockHash, blockHeight
}
blocksMatch = true
}
return nil
}
// ConnectNode establishes a new peer-to-peer connection between the "from"
// harness and the "to" harness. The connection made is flagged as persistent,
// therefore in the case of disconnects, "from" will attempt to reestablish a
// connection to the "to" harness.
func ConnectNode(from *Harness, to *Harness) error {
peerInfo, err := from.Node.GetPeerInfo()
if err != nil {
return err
}
numPeers := len(peerInfo)
targetAddr := to.node.config.listen
if err := from.Node.AddNode(targetAddr, rpcclient.ANAdd); err != nil {
return err
}
// Block until a new connection has been established.
peerInfo, err = from.Node.GetPeerInfo()
if err != nil {
return err
}
for len(peerInfo) <= numPeers {
peerInfo, err = from.Node.GetPeerInfo()
if err != nil {
return err
}
}
return nil
}
// TearDownAll tears down all active test harnesses.
func TearDownAll() error {
harnessStateMtx.Lock()
defer harnessStateMtx.Unlock()
for _, harness := range testInstances {
if err := harness.tearDown(); err != nil {
return err
}
}
return nil
}
// ActiveHarnesses returns a slice of all currently active test harnesses. A
// test harness if considered "active" if it has been created, but not yet torn
// down.
func ActiveHarnesses() []*Harness {
harnessStateMtx.RLock()
defer harnessStateMtx.RUnlock()
activeNodes := make([]*Harness, 0, len(testInstances))
for _, harness := range testInstances {
activeNodes = append(activeNodes, harness)
}
return activeNodes
}

View File

@ -1,127 +0,0 @@
// 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 peer
import (
"bytes"
"container/list"
"fmt"
"sync"
"github.com/btcsuite/btcd/wire"
)
// mruInventoryMap provides a concurrency safe map that is limited to a maximum
// number of items with eviction for the oldest entry when the limit is
// exceeded.
type mruInventoryMap struct {
invMtx sync.Mutex
invMap map[wire.InvVect]*list.Element // nearly O(1) lookups
invList *list.List // O(1) insert, update, delete
limit uint
}
// String returns the map as a human-readable string.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) String() string {
m.invMtx.Lock()
defer m.invMtx.Unlock()
lastEntryNum := len(m.invMap) - 1
curEntry := 0
buf := bytes.NewBufferString("[")
for iv := range m.invMap {
buf.WriteString(fmt.Sprintf("%v", iv))
if curEntry < lastEntryNum {
buf.WriteString(", ")
}
curEntry++
}
buf.WriteString("]")
return fmt.Sprintf("<%d>%s", m.limit, buf.String())
}
// Exists returns whether or not the passed inventory item is in the map.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Exists(iv *wire.InvVect) bool {
m.invMtx.Lock()
_, exists := m.invMap[*iv]
m.invMtx.Unlock()
return exists
}
// Add adds the passed inventory to the map and handles eviction of the oldest
// item if adding the new item would exceed the max limit. Adding an existing
// item makes it the most recently used item.
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Add(iv *wire.InvVect) {
m.invMtx.Lock()
defer m.invMtx.Unlock()
// When the limit is zero, nothing can be added to the map, so just
// return.
if m.limit == 0 {
return
}
// When the entry already exists move it to the front of the list
// thereby marking it most recently used.
if node, exists := m.invMap[*iv]; exists {
m.invList.MoveToFront(node)
return
}
// Evict the least recently used entry (back of the list) if the the new
// entry would exceed the size limit for the map. Also reuse the list
// node so a new one doesn't have to be allocated.
if uint(len(m.invMap))+1 > m.limit {
node := m.invList.Back()
lru := node.Value.(*wire.InvVect)
// Evict least recently used item.
delete(m.invMap, *lru)
// Reuse the list node of the item that was just evicted for the
// new item.
node.Value = iv
m.invList.MoveToFront(node)
m.invMap[*iv] = node
return
}
// The limit hasn't been reached yet, so just add the new item.
node := m.invList.PushFront(iv)
m.invMap[*iv] = node
}
// Delete deletes the passed inventory item from the map (if it exists).
//
// This function is safe for concurrent access.
func (m *mruInventoryMap) Delete(iv *wire.InvVect) {
m.invMtx.Lock()
if node, exists := m.invMap[*iv]; exists {
m.invList.Remove(node)
delete(m.invMap, *iv)
}
m.invMtx.Unlock()
}
// newMruInventoryMap returns a new inventory map that is limited to the number
// of entries specified by limit. When the number of entries exceeds the limit,
// the oldest (least recently used) entry will be removed to make room for the
// new entry.
func newMruInventoryMap(limit uint) *mruInventoryMap {
m := mruInventoryMap{
invMap: make(map[wire.InvVect]*list.Element),
invList: list.New(),
limit: limit,
}
return &m
}

View File

@ -1,125 +0,0 @@
// 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 peer
import (
"bytes"
"container/list"
"fmt"
"sync"
)
// mruNonceMap provides a concurrency safe map that is limited to a maximum
// number of items with eviction for the oldest entry when the limit is
// exceeded.
type mruNonceMap struct {
mtx sync.Mutex
nonceMap map[uint64]*list.Element // nearly O(1) lookups
nonceList *list.List // O(1) insert, update, delete
limit uint
}
// String returns the map as a human-readable string.
//
// This function is safe for concurrent access.
func (m *mruNonceMap) String() string {
m.mtx.Lock()
defer m.mtx.Unlock()
lastEntryNum := len(m.nonceMap) - 1
curEntry := 0
buf := bytes.NewBufferString("[")
for nonce := range m.nonceMap {
buf.WriteString(fmt.Sprintf("%d", nonce))
if curEntry < lastEntryNum {
buf.WriteString(", ")
}
curEntry++
}
buf.WriteString("]")
return fmt.Sprintf("<%d>%s", m.limit, buf.String())
}
// Exists returns whether or not the passed nonce is in the map.
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Exists(nonce uint64) bool {
m.mtx.Lock()
_, exists := m.nonceMap[nonce]
m.mtx.Unlock()
return exists
}
// Add adds the passed nonce to the map and handles eviction of the oldest item
// if adding the new item would exceed the max limit. Adding an existing item
// makes it the most recently used item.
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Add(nonce uint64) {
m.mtx.Lock()
defer m.mtx.Unlock()
// When the limit is zero, nothing can be added to the map, so just
// return.
if m.limit == 0 {
return
}
// When the entry already exists move it to the front of the list
// thereby marking it most recently used.
if node, exists := m.nonceMap[nonce]; exists {
m.nonceList.MoveToFront(node)
return
}
// Evict the least recently used entry (back of the list) if the the new
// entry would exceed the size limit for the map. Also reuse the list
// node so a new one doesn't have to be allocated.
if uint(len(m.nonceMap))+1 > m.limit {
node := m.nonceList.Back()
lru := node.Value.(uint64)
// Evict least recently used item.
delete(m.nonceMap, lru)
// Reuse the list node of the item that was just evicted for the
// new item.
node.Value = nonce
m.nonceList.MoveToFront(node)
m.nonceMap[nonce] = node
return
}
// The limit hasn't been reached yet, so just add the new item.
node := m.nonceList.PushFront(nonce)
m.nonceMap[nonce] = node
}
// Delete deletes the passed nonce from the map (if it exists).
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Delete(nonce uint64) {
m.mtx.Lock()
if node, exists := m.nonceMap[nonce]; exists {
m.nonceList.Remove(node)
delete(m.nonceMap, nonce)
}
m.mtx.Unlock()
}
// newMruNonceMap returns a new nonce map that is limited to the number of
// entries specified by limit. When the number of entries exceeds the limit,
// the oldest (least recently used) entry will be removed to make room for the
// new entry.
func newMruNonceMap(limit uint) *mruNonceMap {
m := mruNonceMap{
nonceMap: make(map[uint64]*list.Element),
nonceList: list.New(),
limit: limit,
}
return &m
}

View File

@ -24,6 +24,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/go-socks/socks"
"github.com/davecgh/go-spew/spew"
"github.com/decred/dcrd/lru"
)
const (
@ -82,7 +83,7 @@ var (
// sentNonces houses the unique nonces that are generated when pushing
// version messages that are used to detect self connections.
sentNonces = newMruNonceMap(50)
sentNonces = lru.NewCache(50)
// allowSelfConns is only used to allow the tests to bypass the self
// connection detecting and disconnect logic since they intentionally
@ -450,7 +451,7 @@ type Peer struct {
wireEncoding wire.MessageEncoding
knownInventory *mruInventoryMap
knownInventory lru.Cache
prevGetBlocksMtx sync.Mutex
prevGetBlocksBegin *chainhash.Hash
prevGetBlocksStop *chainhash.Hash
@ -1626,7 +1627,7 @@ out:
// Don't send inventory that became known after
// the initial check.
if p.knownInventory.Exists(iv) {
if p.knownInventory.Contains(iv) {
continue
}
@ -1832,7 +1833,7 @@ func (p *Peer) QueueMessageWithEncoding(msg wire.Message, doneChan chan<- struct
func (p *Peer) QueueInventory(invVect *wire.InvVect) {
// Don't add the inventory to the send queue if the peer is already
// known to have it.
if p.knownInventory.Exists(invVect) {
if p.knownInventory.Contains(invVect) {
return
}
@ -1891,7 +1892,7 @@ func (p *Peer) readRemoteVersionMsg() error {
}
// Detect self connections.
if !allowSelfConns && sentNonces.Exists(msg.Nonce) {
if !allowSelfConns && sentNonces.Contains(msg.Nonce) {
return errors.New("disconnecting peer connected to self")
}
@ -2097,7 +2098,7 @@ func (p *Peer) negotiateInboundProtocol() error {
return p.readRemoteVerAckMsg()
}
// negotiateOutoundProtocol performs the negotiation protocol for an outbound
// negotiateOutboundProtocol performs the negotiation protocol for an outbound
// peer. The events should occur in the following order, otherwise an error is
// returned:
//
@ -2224,7 +2225,7 @@ func newPeerBase(origCfg *Config, inbound bool) *Peer {
p := Peer{
inbound: inbound,
wireEncoding: wire.BaseEncoding,
knownInventory: newMruInventoryMap(maxKnownInventory),
knownInventory: lru.NewCache(maxKnownInventory),
stallControl: make(chan stallControlMsg, 1), // nonblocking sync
outputQueue: make(chan outMsg, outputBufferSize),
sendQueue: make(chan outMsg, 1), // nonblocking sync

View File

@ -52,14 +52,61 @@ func (c *Client) GetBestBlockHash() (*chainhash.Hash, error) {
return c.GetBestBlockHashAsync().Receive()
}
// legacyGetBlockRequest constructs and sends a legacy getblock request which
// contains two separate bools to denote verbosity, in contract to a single int
// parameter.
func (c *Client) legacyGetBlockRequest(hash string, verbose,
verboseTx bool) ([]byte, error) {
hashJSON, err := json.Marshal(hash)
if err != nil {
return nil, err
}
verboseJSON, err := json.Marshal(btcjson.Bool(verbose))
if err != nil {
return nil, err
}
verboseTxJSON, err := json.Marshal(btcjson.Bool(verboseTx))
if err != nil {
return nil, err
}
return c.RawRequest("getblock", []json.RawMessage{
hashJSON, verboseJSON, verboseTxJSON,
})
}
// waitForGetBlockRes waits for the response of a getblock request. If the
// response indicates an invalid parameter was provided, a legacy style of the
// request is resent and its response is returned instead.
func (c *Client) waitForGetBlockRes(respChan chan *response, hash string,
verbose, verboseTx bool) ([]byte, error) {
res, err := receiveFuture(respChan)
// If we receive an invalid parameter error, then we may be
// communicating with a btcd node which only understands the legacy
// request, so we'll try that.
if err, ok := err.(*btcjson.RPCError); ok &&
err.Code == btcjson.ErrRPCInvalidParams.Code {
return c.legacyGetBlockRequest(hash, verbose, verboseTx)
}
// Otherwise, we can return the response as is.
return res, err
}
// FutureGetBlockResult is a future promise to deliver the result of a
// GetBlockAsync RPC invocation (or an applicable error).
type FutureGetBlockResult chan *response
type FutureGetBlockResult struct {
client *Client
hash string
Response 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)
res, err := r.client.waitForGetBlockRes(r.Response, r.hash, false, false)
if err != nil {
return nil, err
}
@ -97,8 +144,12 @@ func (c *Client) GetBlockAsync(blockHash *chainhash.Hash) FutureGetBlockResult {
hash = blockHash.String()
}
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(false), nil)
return c.sendCmd(cmd)
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(0))
return FutureGetBlockResult{
client: c,
hash: hash,
Response: c.sendCmd(cmd),
}
}
// GetBlock returns a raw block from the server given its hash.
@ -111,12 +162,16 @@ func (c *Client) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
// FutureGetBlockVerboseResult is a future promise to deliver the result of a
// GetBlockVerboseAsync RPC invocation (or an applicable error).
type FutureGetBlockVerboseResult chan *response
type FutureGetBlockVerboseResult struct {
client *Client
hash string
Response 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)
res, err := r.client.waitForGetBlockRes(r.Response, r.hash, true, false)
if err != nil {
return nil, err
}
@ -140,9 +195,14 @@ func (c *Client) GetBlockVerboseAsync(blockHash *chainhash.Hash) FutureGetBlockV
if blockHash != nil {
hash = blockHash.String()
}
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), nil)
return c.sendCmd(cmd)
// From the bitcoin-cli getblock documentation:
// "If verbosity is 1, returns an Object with information about block ."
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(1))
return FutureGetBlockVerboseResult{
client: c,
hash: hash,
Response: c.sendCmd(cmd),
}
}
// GetBlockVerbose returns a data structure from the server with information
@ -154,19 +214,52 @@ func (c *Client) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.GetBlockVe
return c.GetBlockVerboseAsync(blockHash).Receive()
}
// FutureGetBlockVerboseTxResult is a future promise to deliver the result of a
// GetBlockVerboseTxResult RPC invocation (or an applicable error).
type FutureGetBlockVerboseTxResult struct {
client *Client
hash string
Response chan *response
}
// Receive waits for the response promised by the future and returns a verbose
// version of the block including detailed information about its transactions.
func (r FutureGetBlockVerboseTxResult) Receive() (*btcjson.GetBlockVerboseTxResult, error) {
res, err := r.client.waitForGetBlockRes(r.Response, r.hash, true, true)
if err != nil {
return nil, err
}
var blockResult btcjson.GetBlockVerboseTxResult
err = json.Unmarshal(res, &blockResult)
if err != nil {
return nil, err
}
return &blockResult, nil
}
// 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 {
func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBlockVerboseTxResult {
hash := ""
if blockHash != nil {
hash = blockHash.String()
}
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Bool(true), btcjson.Bool(true))
return c.sendCmd(cmd)
// From the bitcoin-cli getblock documentation:
//
// If verbosity is 2, returns an Object with information about block
// and information about each transaction.
cmd := btcjson.NewGetBlockCmd(hash, btcjson.Int(2))
return FutureGetBlockVerboseTxResult{
client: c,
hash: hash,
Response: c.sendCmd(cmd),
}
}
// GetBlockVerboseTx returns a data structure from the server with information
@ -174,7 +267,7 @@ func (c *Client) GetBlockVerboseTxAsync(blockHash *chainhash.Hash) FutureGetBloc
//
// 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) {
func (c *Client) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseTxResult, error) {
return c.GetBlockVerboseTxAsync(blockHash).Receive()
}
@ -214,6 +307,79 @@ func (c *Client) GetBlockCount() (int64, error) {
return c.GetBlockCountAsync().Receive()
}
// FutureGetChainTxStatsResult is a future promise to deliver the result of a
// GetChainTxStatsAsync RPC invocation (or an applicable error).
type FutureGetChainTxStatsResult chan *response
// Receive waits for the response promised by the future and returns transaction statistics
func (r FutureGetChainTxStatsResult) Receive() (*btcjson.GetChainTxStatsResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
var chainTxStats btcjson.GetChainTxStatsResult
err = json.Unmarshal(res, &chainTxStats)
if err != nil {
return nil, err
}
return &chainTxStats, nil
}
// GetChainTxStatsAsync 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 GetChainTxStats for the blocking version and more details.
func (c *Client) GetChainTxStatsAsync() FutureGetChainTxStatsResult {
cmd := btcjson.NewGetChainTxStatsCmd(nil, nil)
return c.sendCmd(cmd)
}
// GetChainTxStatsNBlocksAsync 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 GetChainTxStatsNBlocks for the blocking version and more details.
func (c *Client) GetChainTxStatsNBlocksAsync(nBlocks int32) FutureGetChainTxStatsResult {
cmd := btcjson.NewGetChainTxStatsCmd(&nBlocks, nil)
return c.sendCmd(cmd)
}
// GetChainTxStatsNBlocksBlockHashAsync 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 GetChainTxStatsNBlocksBlockHash for the blocking version and more details.
func (c *Client) GetChainTxStatsNBlocksBlockHashAsync(nBlocks int32, blockHash chainhash.Hash) FutureGetChainTxStatsResult {
hash := blockHash.String()
cmd := btcjson.NewGetChainTxStatsCmd(&nBlocks, &hash)
return c.sendCmd(cmd)
}
// GetChainTxStats returns statistics about the total number and rate of transactions in the chain.
//
// Size of the window is one month and it ends at chain tip.
func (c *Client) GetChainTxStats() (*btcjson.GetChainTxStatsResult, error) {
return c.GetChainTxStatsAsync().Receive()
}
// GetChainTxStatsNBlocks returns statistics about the total number and rate of transactions in the chain.
//
// The argument specifies size of the window in number of blocks. The window ends at chain tip.
func (c *Client) GetChainTxStatsNBlocks(nBlocks int32) (*btcjson.GetChainTxStatsResult, error) {
return c.GetChainTxStatsNBlocksAsync(nBlocks).Receive()
}
// GetChainTxStatsNBlocksBlockHash returns statistics about the total number and rate of transactions in the chain.
//
// First argument specifies size of the window in number of blocks.
// Second argument is the hash of the block that ends the window.
func (c *Client) GetChainTxStatsNBlocksBlockHash(nBlocks int32, blockHash chainhash.Hash) (*btcjson.GetChainTxStatsResult, error) {
return c.GetChainTxStatsNBlocksBlockHashAsync(nBlocks, blockHash).Receive()
}
// FutureGetDifficultyResult is a future promise to deliver the result of a
// GetDifficultyAsync RPC invocation (or an applicable error).
type FutureGetDifficultyResult chan *response
@ -649,6 +815,41 @@ func (c *Client) EstimateFee(numBlocks int64) (float64, error) {
return c.EstimateFeeAsync(numBlocks).Receive()
}
// FutureEstimateFeeResult is a future promise to deliver the result of a
// EstimateSmartFeeAsync RPC invocation (or an applicable error).
type FutureEstimateSmartFeeResult chan *response
// Receive waits for the response promised by the future and returns the
// estimated fee.
func (r FutureEstimateSmartFeeResult) Receive() (*btcjson.EstimateSmartFeeResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
var verified btcjson.EstimateSmartFeeResult
err = json.Unmarshal(res, &verified)
if err != nil {
return nil, err
}
return &verified, nil
}
// EstimateSmartFeeAsync 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 EstimateSmartFee for the blocking version and more details.
func (c *Client) EstimateSmartFeeAsync(confTarget int64, mode *btcjson.EstimateSmartFeeMode) FutureEstimateSmartFeeResult {
cmd := btcjson.NewEstimateSmartFeeCmd(confTarget, mode)
return c.sendCmd(cmd)
}
// EstimateSmartFee requests the server to estimate a fee level based on the given parameters.
func (c *Client) EstimateSmartFee(confTarget int64, mode *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) {
return c.EstimateSmartFeeAsync(confTarget, mode).Receive()
}
// FutureVerifyChainResult is a future promise to deliver the result of a
// VerifyChainAsync, VerifyChainLevelAsyncRPC, or VerifyChainBlocksAsync
// invocation (or an applicable error).
@ -982,3 +1183,44 @@ func (c *Client) GetCFilterHeader(blockHash *chainhash.Hash,
filterType wire.FilterType) (*wire.MsgCFHeaders, error) {
return c.GetCFilterHeaderAsync(blockHash, filterType).Receive()
}
// FutureGetBlockStatsResult is a future promise to deliver the result of a
// GetBlockStatsAsync RPC invocation (or an applicable error).
type FutureGetBlockStatsResult chan *response
// Receive waits for the response promised by the future and returns statistics
// of a block at a certain height.
func (r FutureGetBlockStatsResult) Receive() (*btcjson.GetBlockStatsResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
var blockStats btcjson.GetBlockStatsResult
err = json.Unmarshal(res, &blockStats)
if err != nil {
return nil, err
}
return &blockStats, nil
}
// GetBlockStatsAsync 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 GetBlockStats or the blocking version and more details.
func (c *Client) GetBlockStatsAsync(hashOrHeight interface{}, stats *[]string) FutureGetBlockStatsResult {
if hash, ok := hashOrHeight.(*chainhash.Hash); ok {
hashOrHeight = hash.String()
}
cmd := btcjson.NewGetBlockStatsCmd(btcjson.HashOrHeight{Value: hashOrHeight}, stats)
return c.sendCmd(cmd)
}
// GetBlockStats returns block statistics. First argument specifies height or hash of the target block.
// Second argument allows to select certain stats to return.
func (c *Client) GetBlockStats(hashOrHeight interface{}, stats *[]string) (*btcjson.GetBlockStatsResult, error) {
return c.GetBlockStatsAsync(hashOrHeight, stats).Receive()
}

View File

@ -0,0 +1,38 @@
// Copyright (c) 2017 The Namecoin developers
// Copyright (c) 2019 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 (
"bufio"
"fmt"
"os"
"strings"
)
func readCookieFile(path string) (username, password string, err error) {
f, err := os.Open(path)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Scan()
err = scanner.Err()
if err != nil {
return
}
s := scanner.Text()
parts := strings.SplitN(s, ":", 2)
if len(parts) != 2 {
err = fmt.Errorf("malformed cookie file")
return
}
username, password = parts[0], parts[1]
return
}

View File

@ -106,7 +106,7 @@ 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
from the server. An effort has been made to call out which commands are
extensions in their documentation.
Also, it is important to realize that btcd intentionally separates the wallet

View File

@ -19,12 +19,14 @@ import (
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/go-socks/socks"
"github.com/btcsuite/websocket"
)
@ -138,6 +140,10 @@ type Client struct {
// config holds the connection configuration assoiated with this client.
config *ConnConfig
// chainParams holds the params for the chain that this client is using,
// and is used for many wallet methods.
chainParams *chaincfg.Params
// wsConn is the underlying websocket connection when not in HTTP POST
// mode.
wsConn *websocket.Conn
@ -283,31 +289,29 @@ func (c *Client) trackRegisteredNtfns(cmd interface{}) {
}
}
type (
// inMessage is the first type that an incoming message is unmarshaled
// into. It supports both requests (for notification support) and
// responses. The partially-unmarshaled message is a notification if
// the embedded ID (from the response) is nil. Otherwise, it is a
// response.
inMessage struct {
ID *float64 `json:"id"`
*rawNotification
*rawResponse
}
// inMessage is the first type that an incoming message is unmarshaled
// into. It supports both requests (for notification support) and
// responses. The partially-unmarshaled message is a notification if
// the embedded ID (from the response) is nil. Otherwise, it is a
// response.
type inMessage struct {
ID *float64 `json:"id"`
*rawNotification
*rawResponse
}
// rawNotification is a partially-unmarshaled JSON-RPC notification.
rawNotification struct {
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
}
// rawNotification is a partially-unmarshaled JSON-RPC notification.
type rawNotification struct {
Method string `json:"method"`
Params []json.RawMessage `json:"params"`
}
// rawResponse is a partially-unmarshaled JSON-RPC response. For this
// to be valid (according to JSON-RPC 1.0 spec), ID may not be nil.
rawResponse struct {
Result json.RawMessage `json:"result"`
Error *btcjson.RPCError `json:"error"`
}
)
// rawResponse is a partially-unmarshaled JSON-RPC response. For this
// to be valid (according to JSON-RPC 1.0 spec), ID may not be nil.
type rawResponse struct {
Result json.RawMessage `json:"result"`
Error *btcjson.RPCError `json:"error"`
}
// response is the raw bytes of a JSON-RPC result, or the error if the response
// error object was non-null.
@ -848,7 +852,12 @@ func (c *Client) sendPost(jReq *jsonRequest) {
httpReq.Header.Set("Content-Type", "application/json")
// Configure basic access authorization.
httpReq.SetBasicAuth(c.config.User, c.config.Pass)
user, pass, err := c.config.getAuth()
if err != nil {
jReq.responseChan <- &response{result: nil, err: err}
return
}
httpReq.SetBasicAuth(user, pass)
log.Tracef("Sending command [%s] with id %d", jReq.method, jReq.id)
c.sendPostRequest(httpReq, jReq)
@ -1093,6 +1102,22 @@ type ConnConfig struct {
// Pass is the passphrase to use to authenticate to the RPC server.
Pass string
// CookiePath is the path to a cookie file containing the username and
// passphrase to use to authenticate to the RPC server. It is used
// instead of User and Pass if non-empty.
CookiePath string
cookieLastCheckTime time.Time
cookieLastModTime time.Time
cookieLastUser string
cookieLastPass string
cookieLastErr error
// Params is the string representing the network that the server
// is running. If there is no parameter set in the config, then
// mainnet will be used by default.
Params string
// DisableTLS specifies whether transport layer security should be
// disabled. It is recommended to always use TLS if the RPC server
// supports it as otherwise your username and password is sent across
@ -1141,6 +1166,43 @@ type ConnConfig struct {
EnableBCInfoHacks bool
}
// getAuth returns the username and passphrase that will actually be used for
// this connection. This will be the result of checking the cookie if a cookie
// path is configured; if not, it will be the user-configured username and
// passphrase.
func (config *ConnConfig) getAuth() (username, passphrase string, err error) {
// Try username+passphrase auth first.
if config.Pass != "" {
return config.User, config.Pass, nil
}
// If no username or passphrase is set, try cookie auth.
return config.retrieveCookie()
}
// retrieveCookie returns the cookie username and passphrase.
func (config *ConnConfig) retrieveCookie() (username, passphrase string, err error) {
if !config.cookieLastCheckTime.IsZero() && time.Now().Before(config.cookieLastCheckTime.Add(30*time.Second)) {
return config.cookieLastUser, config.cookieLastPass, config.cookieLastErr
}
config.cookieLastCheckTime = time.Now()
st, err := os.Stat(config.CookiePath)
if err != nil {
config.cookieLastErr = err
return config.cookieLastUser, config.cookieLastPass, config.cookieLastErr
}
modTime := st.ModTime()
if !modTime.Equal(config.cookieLastModTime) {
config.cookieLastModTime = modTime
config.cookieLastUser, config.cookieLastPass, config.cookieLastErr = readCookieFile(config.CookiePath)
}
return config.cookieLastUser, config.cookieLastPass, config.cookieLastErr
}
// newHTTPClient returns a new http client that is configured according to the
// proxy and TLS settings in the associated connection configuration.
func newHTTPClient(config *ConnConfig) (*http.Client, error) {
@ -1210,7 +1272,11 @@ func dial(config *ConnConfig) (*websocket.Conn, error) {
// The RPC server requires basic authorization, so create a custom
// request header with the Authorization header set.
login := config.User + ":" + config.Pass
user, pass, err := config.getAuth()
if err != nil {
return nil, err
}
login := user + ":" + pass
auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(login))
requestHeader := make(http.Header)
requestHeader.Add("Authorization", auth)
@ -1290,6 +1356,23 @@ func New(config *ConnConfig, ntfnHandlers *NotificationHandlers) (*Client, error
shutdown: make(chan struct{}),
}
// Default network is mainnet, no parameters are necessary but if mainnet
// is specified it will be the param
switch config.Params {
case "":
fallthrough
case chaincfg.MainNetParams.Name:
client.chainParams = &chaincfg.MainNetParams
case chaincfg.TestNet3Params.Name:
client.chainParams = &chaincfg.TestNet3Params
case chaincfg.RegressionNetParams.Name:
client.chainParams = &chaincfg.RegressionNetParams
case chaincfg.SimNetParams.Name:
client.chainParams = &chaincfg.SimNetParams
default:
return nil, fmt.Errorf("rpcclient.New: Unknown chain %s", config.Params)
}
if start {
log.Infof("Established connection to RPC server %s",
config.Host)

View File

@ -61,6 +61,53 @@ func (c *Client) Generate(numBlocks uint32) ([]*chainhash.Hash, error) {
return c.GenerateAsync(numBlocks).Receive()
}
// FutureGenerateToAddressResult is a future promise to deliver the result of a
// GenerateToAddressResult RPC invocation (or an applicable error).
type FutureGenerateToAddressResult chan *response
// Receive waits for the response promised by the future and returns the hashes of
// of the generated blocks.
func (f FutureGenerateToAddressResult) Receive() ([]*chainhash.Hash, error) {
res, err := receiveFuture(f)
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
}
// GenerateToAddressAsync 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 GenerateToAddress for the blocking version and more details.
func (c *Client) GenerateToAddressAsync(numBlocks int64, address btcutil.Address, maxTries *int64) FutureGenerateToAddressResult {
cmd := btcjson.NewGenerateToAddressCmd(numBlocks, address.EncodeAddress(), maxTries)
return c.sendCmd(cmd)
}
// GenerateToAddress generates numBlocks blocks to the given address and returns their hashes.
func (c *Client) GenerateToAddress(numBlocks int64, address btcutil.Address, maxTries *int64) ([]*chainhash.Hash, error) {
return c.GenerateToAddressAsync(numBlocks, address, maxTries).Receive()
}
// FutureGetGenerateResult is a future promise to deliver the result of a
// GetGenerateAsync RPC invocation (or an applicable error).
type FutureGetGenerateResult chan *response

View File

@ -95,7 +95,7 @@ type NotificationHandlers struct {
// NotifyBlocks has been made to register for the notification and the
// function is non-nil.
//
// NOTE: Deprecated. Use OnFilteredBlockConnected instead.
// Deprecated: Use OnFilteredBlockConnected instead.
OnBlockConnected func(hash *chainhash.Hash, height int32, t time.Time)
// OnFilteredBlockConnected is invoked when a block is connected to the
@ -111,7 +111,7 @@ type NotificationHandlers struct {
// NotifyBlocks has been made to register for the notification and the
// function is non-nil.
//
// NOTE: Deprecated. Use OnFilteredBlockDisconnected instead.
// Deprecated: Use OnFilteredBlockDisconnected instead.
OnBlockDisconnected func(hash *chainhash.Hash, height int32, t time.Time)
// OnFilteredBlockDisconnected is invoked when a block is disconnected
@ -127,7 +127,7 @@ type NotificationHandlers struct {
// preceding call to NotifyReceived, Rescan, or RescanEndHeight has been
// made to register for the notification and the function is non-nil.
//
// NOTE: Deprecated. Use OnRelevantTxAccepted instead.
// Deprecated: Use OnRelevantTxAccepted instead.
OnRecvTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails)
// OnRedeemingTx is invoked when a transaction that spends a registered
@ -141,7 +141,7 @@ type NotificationHandlers struct {
// funds to the registered addresses. This means it is possible for
// this to invoked indirectly as the result of a NotifyReceived call.
//
// NOTE: Deprecated. Use OnRelevantTxAccepted instead.
// Deprecated: Use OnRelevantTxAccepted instead.
OnRedeemingTx func(transaction *btcutil.Tx, details *btcjson.BlockDetails)
// OnRelevantTxAccepted is invoked when an unmined transaction passes
@ -157,14 +157,14 @@ type NotificationHandlers struct {
// result of a rescan request, due to how btcd may send various rescan
// notifications after the rescan request has already returned.
//
// NOTE: Deprecated. Not used with RescanBlocks.
// Deprecated: Not used with RescanBlocks.
OnRescanFinished func(hash *chainhash.Hash, height int32, blkTime time.Time)
// OnRescanProgress is invoked periodically when a rescan is underway.
// It will only be invoked if a preceding call to Rescan or
// RescanEndHeight has been made and the function is non-nil.
//
// NOTE: Deprecated. Not used with RescanBlocks.
// Deprecated: Not used with RescanBlocks.
OnRescanProgress func(hash *chainhash.Hash, height int32, blkTime time.Time)
// OnTxAccepted is invoked when a transaction is accepted into the
@ -905,7 +905,7 @@ func (c *Client) NotifyBlocks() error {
// FutureNotifySpentResult is a future promise to deliver the result of a
// NotifySpentAsync RPC invocation (or an applicable error).
//
// NOTE: Deprecated. Use FutureLoadTxFilterResult instead.
// Deprecated: Use FutureLoadTxFilterResult instead.
type FutureNotifySpentResult chan *response
// Receive waits for the response promised by the future and returns an error
@ -951,7 +951,7 @@ func newOutPointFromWire(op *wire.OutPoint) btcjson.OutPoint {
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use LoadTxFilterAsync instead.
// Deprecated: Use LoadTxFilterAsync instead.
func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentResult {
// Not supported in HTTP POST mode.
if c.config.HTTPPostMode {
@ -983,7 +983,7 @@ func (c *Client) NotifySpentAsync(outpoints []*wire.OutPoint) FutureNotifySpentR
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use LoadTxFilter instead.
// Deprecated: Use LoadTxFilter instead.
func (c *Client) NotifySpent(outpoints []*wire.OutPoint) error {
return c.NotifySpentAsync(outpoints).Receive()
}
@ -1040,7 +1040,7 @@ func (c *Client) NotifyNewTransactions(verbose bool) error {
// FutureNotifyReceivedResult is a future promise to deliver the result of a
// NotifyReceivedAsync RPC invocation (or an applicable error).
//
// NOTE: Deprecated. Use FutureLoadTxFilterResult instead.
// Deprecated: Use FutureLoadTxFilterResult instead.
type FutureNotifyReceivedResult chan *response
// Receive waits for the response promised by the future and returns an error
@ -1078,7 +1078,7 @@ func (c *Client) notifyReceivedInternal(addresses []string) FutureNotifyReceived
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use LoadTxFilterAsync instead.
// Deprecated: Use LoadTxFilterAsync instead.
func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyReceivedResult {
// Not supported in HTTP POST mode.
if c.config.HTTPPostMode {
@ -1118,7 +1118,7 @@ func (c *Client) NotifyReceivedAsync(addresses []btcutil.Address) FutureNotifyRe
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use LoadTxFilter instead.
// Deprecated: Use LoadTxFilter instead.
func (c *Client) NotifyReceived(addresses []btcutil.Address) error {
return c.NotifyReceivedAsync(addresses).Receive()
}
@ -1126,7 +1126,7 @@ func (c *Client) NotifyReceived(addresses []btcutil.Address) error {
// FutureRescanResult is a future promise to deliver the result of a RescanAsync
// or RescanEndHeightAsync RPC invocation (or an applicable error).
//
// NOTE: Deprecated. Use FutureRescanBlocksResult instead.
// Deprecated: Use FutureRescanBlocksResult instead.
type FutureRescanResult chan *response
// Receive waits for the response promised by the future and returns an error
@ -1150,7 +1150,7 @@ func (r FutureRescanResult) Receive() error {
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use RescanBlocksAsync instead.
// Deprecated: Use RescanBlocksAsync instead.
func (c *Client) RescanAsync(startBlock *chainhash.Hash,
addresses []btcutil.Address,
outpoints []*wire.OutPoint) FutureRescanResult {
@ -1215,7 +1215,7 @@ func (c *Client) RescanAsync(startBlock *chainhash.Hash,
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use RescanBlocks instead.
// Deprecated: Use RescanBlocks instead.
func (c *Client) Rescan(startBlock *chainhash.Hash,
addresses []btcutil.Address,
outpoints []*wire.OutPoint) error {
@ -1231,7 +1231,7 @@ func (c *Client) Rescan(startBlock *chainhash.Hash,
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use RescanBlocksAsync instead.
// Deprecated: Use RescanBlocksAsync instead.
func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash,
addresses []btcutil.Address, outpoints []*wire.OutPoint,
endBlock *chainhash.Hash) FutureRescanResult {
@ -1293,7 +1293,7 @@ func (c *Client) RescanEndBlockAsync(startBlock *chainhash.Hash,
//
// NOTE: This is a btcd extension and requires a websocket connection.
//
// NOTE: Deprecated. Use RescanBlocks instead.
// Deprecated: Use RescanBlocks instead.
func (c *Client) RescanEndHeight(startBlock *chainhash.Hash,
addresses []btcutil.Address, outpoints []*wire.OutPoint,
endBlock *chainhash.Hash) error {

View File

@ -205,6 +205,47 @@ func (c *Client) DecodeRawTransaction(serializedTx []byte) (*btcjson.TxRawResult
return c.DecodeRawTransactionAsync(serializedTx).Receive()
}
// FutureFundRawTransactionResult is a future promise to deliver the result
// of a FutureFundRawTransactionAsync RPC invocation (or an applicable error).
type FutureFundRawTransactionResult chan *response
// Receive waits for the response promised by the future and returns information
// about a funding attempt
func (r FutureFundRawTransactionResult) Receive() (*btcjson.FundRawTransactionResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
var marshalled btcjson.FundRawTransactionResult
if err := json.Unmarshal(res, &marshalled); err != nil {
return nil, err
}
return &marshalled, nil
}
// FundRawTransactionAsync 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 FundRawTransaction for the blocking version and more details.
func (c *Client) FundRawTransactionAsync(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) FutureFundRawTransactionResult {
var txBuf bytes.Buffer
if err := tx.Serialize(&txBuf); err != nil {
return newFutureError(err)
}
cmd := btcjson.NewFundRawTransactionCmd(txBuf.Bytes(), opts, isWitness)
return c.sendCmd(cmd)
}
// FundRawTransaction returns the result of trying to fund the given transaction with
// funds from the node wallet
func (c *Client) FundRawTransaction(tx *wire.MsgTx, opts btcjson.FundRawTransactionOpts, isWitness *bool) (*btcjson.FundRawTransactionResult, error) {
return c.FundRawTransactionAsync(tx, opts, isWitness).Receive()
}
// FutureCreateRawTransactionResult is a future promise to deliver the result
// of a CreateRawTransactionAsync RPC invocation (or an applicable error).
type FutureCreateRawTransactionResult chan *response
@ -233,8 +274,13 @@ func (r FutureCreateRawTransactionResult) Receive() (*wire.MsgTx, error) {
// Deserialize the transaction and return it.
var msgTx wire.MsgTx
if err := msgTx.Deserialize(bytes.NewReader(serializedTx)); err != nil {
return nil, err
// we try both the new and old encoding format
witnessErr := msgTx.Deserialize(bytes.NewReader(serializedTx))
if witnessErr != nil {
legacyErr := msgTx.DeserializeNoWitness(bytes.NewReader(serializedTx))
if legacyErr != nil {
return nil, legacyErr
}
}
return &msgTx, nil
}
@ -256,7 +302,8 @@ func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput,
}
// CreateRawTransaction returns a new transaction spending the provided inputs
// and sending to the provided addresses.
// and sending to the provided addresses. If the inputs are either nil or an
// empty slice, it is interpreted as an empty slice.
func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput,
amounts map[btcutil.Address]btcutil.Amount, lockTime *int64) (*wire.MsgTx, error) {

View File

@ -20,7 +20,8 @@ import (
// *****************************
// FutureGetTransactionResult is a future promise to deliver the result
// of a GetTransactionAsync RPC invocation (or an applicable error).
// of a GetTransactionAsync or GetTransactionWatchOnlyAsync RPC invocation
// (or an applicable error).
type FutureGetTransactionResult chan *response
// Receive waits for the response promised by the future and returns detailed
@ -63,6 +64,28 @@ func (c *Client) GetTransaction(txHash *chainhash.Hash) (*btcjson.GetTransaction
return c.GetTransactionAsync(txHash).Receive()
}
// GetTransactionWatchOnlyAsync 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 GetTransactionWatchOnly for the blocking version and more details.
func (c *Client) GetTransactionWatchOnlyAsync(txHash *chainhash.Hash, watchOnly bool) FutureGetTransactionResult {
hash := ""
if txHash != nil {
hash = txHash.String()
}
cmd := btcjson.NewGetTransactionCmd(hash, &watchOnly)
return c.sendCmd(cmd)
}
// GetTransactionWatchOnly returns detailed information about a wallet
// transaction, and allow including watch-only addresses in balance
// calculation and details.
func (c *Client) GetTransactionWatchOnly(txHash *chainhash.Hash, watchOnly bool) (*btcjson.GetTransactionResult, error) {
return c.GetTransactionWatchOnlyAsync(txHash, watchOnly).Receive()
}
// FutureListTransactionsResult is a future promise to deliver the result of a
// ListTransactionsAsync, ListTransactionsCountAsync, or
// ListTransactionsCountFromAsync RPC invocation (or an applicable error).
@ -753,13 +776,16 @@ func (c *Client) SendManyComment(fromAccount string,
// FutureAddMultisigAddressResult is a future promise to deliver the result of a
// AddMultisigAddressAsync RPC invocation (or an applicable error).
type FutureAddMultisigAddressResult chan *response
type FutureAddMultisigAddressResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns the
// multisignature address that requires the specified number of signatures for
// the provided addresses.
func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -771,7 +797,7 @@ func (r FutureAddMultisigAddressResult) Receive() (btcutil.Address, error) {
return nil, err
}
return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams)
return btcutil.DecodeAddress(addr, r.network)
}
// AddMultisigAddressAsync returns an instance of a type that can be used to get
@ -786,14 +812,17 @@ func (c *Client) AddMultisigAddressAsync(requiredSigs int, addresses []btcutil.A
}
cmd := btcjson.NewAddMultisigAddressCmd(requiredSigs, addrs, &account)
return c.sendCmd(cmd)
result := FutureAddMultisigAddressResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return result
}
// AddMultisigAddress adds a multisignature address that requires the specified
// number of signatures for the provided addresses to the wallet.
func (c *Client) AddMultisigAddress(requiredSigs int, addresses []btcutil.Address, account string) (btcutil.Address, error) {
return c.AddMultisigAddressAsync(requiredSigs, addresses,
account).Receive()
return c.AddMultisigAddressAsync(requiredSigs, addresses, account).Receive()
}
// FutureCreateMultisigResult is a future promise to deliver the result of a
@ -868,12 +897,15 @@ func (c *Client) CreateNewAccount(account string) error {
// FutureGetNewAddressResult is a future promise to deliver the result of a
// GetNewAddressAsync RPC invocation (or an applicable error).
type FutureGetNewAddressResult chan *response
type FutureGetNewAddressResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns a new
// address.
func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -885,7 +917,7 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) {
return nil, err
}
return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams)
return btcutil.DecodeAddress(addr, r.network)
}
// GetNewAddressAsync returns an instance of a type that can be used to get the
@ -895,23 +927,31 @@ func (r FutureGetNewAddressResult) Receive() (btcutil.Address, error) {
// See GetNewAddress for the blocking version and more details.
func (c *Client) GetNewAddressAsync(account string) FutureGetNewAddressResult {
cmd := btcjson.NewGetNewAddressCmd(&account)
return c.sendCmd(cmd)
result := FutureGetNewAddressResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return result
}
// GetNewAddress returns a new address.
// GetNewAddress returns a new address, and decodes based on the client's
// chain params.
func (c *Client) GetNewAddress(account string) (btcutil.Address, error) {
return c.GetNewAddressAsync(account).Receive()
}
// FutureGetRawChangeAddressResult is a future promise to deliver the result of
// a GetRawChangeAddressAsync RPC invocation (or an applicable error).
type FutureGetRawChangeAddressResult chan *response
type FutureGetRawChangeAddressResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns a new
// address for receiving change that will be associated with the provided
// account. Note that this is only for raw transactions and NOT for normal use.
func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -923,7 +963,7 @@ func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) {
return nil, err
}
return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams)
return btcutil.DecodeAddress(addr, r.network)
}
// GetRawChangeAddressAsync returns an instance of a type that can be used to
@ -933,7 +973,11 @@ func (r FutureGetRawChangeAddressResult) Receive() (btcutil.Address, error) {
// See GetRawChangeAddress for the blocking version and more details.
func (c *Client) GetRawChangeAddressAsync(account string) FutureGetRawChangeAddressResult {
cmd := btcjson.NewGetRawChangeAddressCmd(&account)
return c.sendCmd(cmd)
result := FutureGetRawChangeAddressResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return result
}
// GetRawChangeAddress returns a new address for receiving change that will be
@ -945,12 +989,15 @@ func (c *Client) GetRawChangeAddress(account string) (btcutil.Address, error) {
// FutureAddWitnessAddressResult is a future promise to deliver the result of
// a AddWitnessAddressAsync RPC invocation (or an applicable error).
type FutureAddWitnessAddressResult chan *response
type FutureAddWitnessAddressResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns the new
// address.
func (r FutureAddWitnessAddressResult) Receive() (btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -962,7 +1009,7 @@ func (r FutureAddWitnessAddressResult) Receive() (btcutil.Address, error) {
return nil, err
}
return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams)
return btcutil.DecodeAddress(addr, r.network)
}
// AddWitnessAddressAsync returns an instance of a type that can be used to get
@ -972,7 +1019,11 @@ func (r FutureAddWitnessAddressResult) Receive() (btcutil.Address, error) {
// See AddWitnessAddress for the blocking version and more details.
func (c *Client) AddWitnessAddressAsync(address string) FutureAddWitnessAddressResult {
cmd := btcjson.NewAddWitnessAddressCmd(address)
return c.sendCmd(cmd)
response := FutureAddWitnessAddressResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return response
}
// AddWitnessAddress adds a witness address for a script and returns the new
@ -983,12 +1034,15 @@ func (c *Client) AddWitnessAddress(address string) (btcutil.Address, error) {
// FutureGetAccountAddressResult is a future promise to deliver the result of a
// GetAccountAddressAsync RPC invocation (or an applicable error).
type FutureGetAccountAddressResult chan *response
type FutureGetAccountAddressResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns the current
// Bitcoin address for receiving payments to the specified account.
func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -1000,7 +1054,7 @@ func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) {
return nil, err
}
return btcutil.DecodeAddress(addr, &chaincfg.MainNetParams)
return btcutil.DecodeAddress(addr, r.network)
}
// GetAccountAddressAsync returns an instance of a type that can be used to get
@ -1010,7 +1064,11 @@ func (r FutureGetAccountAddressResult) Receive() (btcutil.Address, error) {
// See GetAccountAddress for the blocking version and more details.
func (c *Client) GetAccountAddressAsync(account string) FutureGetAccountAddressResult {
cmd := btcjson.NewGetAccountAddressCmd(account)
return c.sendCmd(cmd)
result := FutureGetAccountAddressResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return result
}
// GetAccountAddress returns the current Bitcoin address for receiving payments
@ -1086,12 +1144,15 @@ func (c *Client) SetAccount(address btcutil.Address, account string) error {
// FutureGetAddressesByAccountResult is a future promise to deliver the result
// of a GetAddressesByAccountAsync RPC invocation (or an applicable error).
type FutureGetAddressesByAccountResult chan *response
type FutureGetAddressesByAccountResult struct {
responseChannel chan *response
network *chaincfg.Params
}
// Receive waits for the response promised by the future and returns the list of
// addresses associated with the passed account.
func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error) {
res, err := receiveFuture(r)
res, err := receiveFuture(r.responseChannel)
if err != nil {
return nil, err
}
@ -1103,17 +1164,15 @@ func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error)
return nil, err
}
addrs := make([]btcutil.Address, 0, len(addrStrings))
for _, addrStr := range addrStrings {
addr, err := btcutil.DecodeAddress(addrStr,
&chaincfg.MainNetParams)
addresses := make([]btcutil.Address, len(addrStrings))
for i, addrString := range addrStrings {
addresses[i], err = btcutil.DecodeAddress(addrString, r.network)
if err != nil {
return nil, err
}
addrs = append(addrs, addr)
}
return addrs, nil
return addresses, nil
}
// GetAddressesByAccountAsync returns an instance of a type that can be used to
@ -1123,7 +1182,11 @@ func (r FutureGetAddressesByAccountResult) Receive() ([]btcutil.Address, error)
// See GetAddressesByAccount for the blocking version and more details.
func (c *Client) GetAddressesByAccountAsync(account string) FutureGetAddressesByAccountResult {
cmd := btcjson.NewGetAddressesByAccountCmd(account)
return c.sendCmd(cmd)
result := FutureGetAddressesByAccountResult{
network: c.chainParams,
responseChannel: c.sendCmd(cmd),
}
return result
}
// GetAddressesByAccount returns the list of addresses associated with the
@ -1507,6 +1570,43 @@ func (c *Client) GetBalanceMinConf(account string, minConfirms int) (btcutil.Amo
return c.GetBalanceMinConfAsync(account, minConfirms).Receive()
}
// FutureGetBalancesResult is a future promise to deliver the result of a
// GetBalancesAsync RPC invocation (or an applicable error).
type FutureGetBalancesResult chan *response
// Receive waits for the response promised by the future and returns the
// available balances from the server.
func (r FutureGetBalancesResult) Receive() (*btcjson.GetBalancesResult, error) {
res, err := receiveFuture(r)
if err != nil {
return nil, err
}
// Unmarshal result as a floating point number.
var balances btcjson.GetBalancesResult
err = json.Unmarshal(res, &balances)
if err != nil {
return nil, err
}
return &balances, nil
}
// GetBalancesAsync 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 GetBalances for the blocking version and more details.
func (c *Client) GetBalancesAsync() FutureGetBalancesResult {
cmd := btcjson.NewGetBalancesCmd()
return c.sendCmd(cmd)
}
// GetBalances returns the available balances from the server.
func (c *Client) GetBalances() (*btcjson.GetBalancesResult, error) {
return c.GetBalancesAsync().Receive()
}
// FutureGetReceivedByAccountResult is a future promise to deliver the result of
// a GetReceivedByAccountAsync or GetReceivedByAccountMinConfAsync RPC
// invocation (or an applicable error).

View File

@ -586,7 +586,7 @@ func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error {
if vm.hasFlag(ScriptVerifyWitnessPubKeyType) &&
vm.isWitnessVersionActive(0) && !btcec.IsCompressedPubKey(pubKey) {
str := "only uncompressed keys are accepted post-segwit"
str := "only compressed keys are accepted post-segwit"
return scriptError(ErrWitnessPubKeyType, str)
}

11
vendor/github.com/btcsuite/btcutil/go.mod generated vendored Normal file
View File

@ -0,0 +1,11 @@
module github.com/btcsuite/btcutil
go 1.13
require (
github.com/aead/siphash v1.0.1
github.com/btcsuite/btcd v0.20.1-beta
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d
)

54
vendor/github.com/btcsuite/btcutil/go.sum generated vendored Normal file
View File

@ -0,0 +1,54 @@
github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
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/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

16
vendor/github.com/btcsuite/btcutil/psbt/LICENSE generated vendored Normal file
View File

@ -0,0 +1,16 @@
ISC License
Copyright (c) 2013-2017 The btcsuite developers
Copyright (c) 2016-2017 The Lightning Network 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.

77
vendor/github.com/btcsuite/btcutil/psbt/bip32.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
package psbt
import (
"bytes"
"encoding/binary"
)
// Bip32Derivation encapsulates the data for the input and output
// Bip32Derivation key-value fields.
//
// TODO(roasbeef): use hdkeychain here instead?
type Bip32Derivation struct {
// PubKey is the raw pubkey serialized in compressed format.
PubKey []byte
// MasterKeyFingerprint is the finger print of the master pubkey.
MasterKeyFingerprint uint32
// Bip32Path is the BIP 32 path with child index as a distinct integer.
Bip32Path []uint32
}
// checkValid ensures that the PubKey in the Bip32Derivation struct is valid.
func (pb *Bip32Derivation) checkValid() bool {
return validatePubkey(pb.PubKey)
}
// Bip32Sorter implements sort.Interface for the Bip32Derivation struct.
type Bip32Sorter []*Bip32Derivation
func (s Bip32Sorter) Len() int { return len(s) }
func (s Bip32Sorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s Bip32Sorter) Less(i, j int) bool {
return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0
}
// readBip32Derivation deserializes a byte slice containing chunks of 4 byte
// little endian encodings of uint32 values, the first of which is the
// masterkeyfingerprint and the remainder of which are the derivation path.
func readBip32Derivation(path []byte) (uint32, []uint32, error) {
if len(path)%4 != 0 || len(path)/4-1 < 1 {
return 0, nil, ErrInvalidPsbtFormat
}
masterKeyInt := binary.LittleEndian.Uint32(path[:4])
var paths []uint32
for i := 4; i < len(path); i += 4 {
paths = append(paths, binary.LittleEndian.Uint32(path[i:i+4]))
}
return masterKeyInt, paths, nil
}
// SerializeBIP32Derivation takes a master key fingerprint as defined in BIP32,
// along with a path specified as a list of uint32 values, and returns a
// bytestring specifying the derivation in the format required by BIP174: //
// master key fingerprint (4) || child index (4) || child index (4) || ....
func SerializeBIP32Derivation(masterKeyFingerprint uint32,
bip32Path []uint32) []byte {
var masterKeyBytes [4]byte
binary.LittleEndian.PutUint32(masterKeyBytes[:], masterKeyFingerprint)
derivationPath := make([]byte, 0, 4+4*len(bip32Path))
derivationPath = append(derivationPath, masterKeyBytes[:]...)
for _, path := range bip32Path {
var pathbytes [4]byte
binary.LittleEndian.PutUint32(pathbytes[:], path)
derivationPath = append(derivationPath, pathbytes[:]...)
}
return derivationPath
}

63
vendor/github.com/btcsuite/btcutil/psbt/creator.go generated vendored Normal file
View File

@ -0,0 +1,63 @@
// 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 psbt
import (
"github.com/btcsuite/btcd/wire"
)
// MinTxVersion is the lowest transaction version that we'll permit.
const MinTxVersion = 1
// New on provision of an input and output 'skeleton' for the transaction, a
// new partially populated PBST packet. The populated packet will include the
// unsigned transaction, and the set of known inputs and outputs contained
// within the unsigned transaction. The values of nLockTime, nSequence (per
// input) and transaction version (must be 1 of 2) must be specified here. Note
// that the default nSequence value is wire.MaxTxInSequenceNum. Referencing
// the PSBT BIP, this function serves the roles of teh Creator.
func New(inputs []*wire.OutPoint,
outputs []*wire.TxOut, version int32, nLockTime uint32,
nSequences []uint32) (*Packet, error) {
// Create the new struct; the input and output lists will be empty, the
// unsignedTx object must be constructed and serialized, and that
// serialization should be entered as the only entry for the
// globalKVPairs list.
//
// Ensure that the version of the transaction is greater then our
// minimum allowed transaction version. There must be one sequence
// number per input.
if version < MinTxVersion || len(nSequences) != len(inputs) {
return nil, ErrInvalidPsbtFormat
}
unsignedTx := wire.NewMsgTx(version)
unsignedTx.LockTime = nLockTime
for i, in := range inputs {
unsignedTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *in,
Sequence: nSequences[i],
})
}
for _, out := range outputs {
unsignedTx.AddTxOut(out)
}
// The input and output lists are empty, but there is a list of those
// two lists, and each one must be of length matching the unsigned
// transaction; the unknown list can be nil.
pInputs := make([]PInput, len(unsignedTx.TxIn))
pOutputs := make([]POutput, len(unsignedTx.TxOut))
// This new Psbt is "raw" and contains no key-value fields, so sanity
// checking with c.Cpsbt.SanityCheck() is not required.
return &Packet{
UnsignedTx: unsignedTx,
Inputs: pInputs,
Outputs: pOutputs,
Unknowns: nil,
}, nil
}

81
vendor/github.com/btcsuite/btcutil/psbt/extractor.go generated vendored Normal file
View File

@ -0,0 +1,81 @@
// 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 psbt
// The Extractor requires provision of a single PSBT
// in which all necessary signatures are encoded, and
// uses it to construct a fully valid network serialized
// transaction.
import (
"bytes"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// Extract takes a finalized psbt.Packet and outputs a finalized transaction
// instance. Note that if the PSBT is in-complete, then an error
// ErrIncompletePSBT will be returned. As the extracted transaction has been
// fully finalized, it will be ready for network broadcast once returned.
func Extract(p *Packet) (*wire.MsgTx, error) {
// If the packet isn't complete, then we'll return an error as it
// doesn't have all the required witness data.
if !p.IsComplete() {
return nil, ErrIncompletePSBT
}
// First, we'll make a copy of the underlying unsigned transaction (the
// initial template) so we don't mutate it during our activates below.
finalTx := p.UnsignedTx.Copy()
// For each input, we'll now populate any relevant witness and
// sigScript data.
for i, tin := range finalTx.TxIn {
// We'll grab the corresponding internal packet input which
// matches this materialized transaction input and emplace that
// final sigScript (if present).
pInput := p.Inputs[i]
if pInput.FinalScriptSig != nil {
tin.SignatureScript = pInput.FinalScriptSig
}
// Similarly, if there's a final witness, then we'll also need
// to extract that as well, parsing the lower-level transaction
// encoding.
if pInput.FinalScriptWitness != nil {
// In order to set the witness, need to re-deserialize
// the field as encoded within the PSBT packet. For
// each input, the witness is encoded as a stack with
// one or more items.
witnessReader := bytes.NewReader(
pInput.FinalScriptWitness,
)
// First we extract the number of witness elements
// encoded in the above witnessReader.
witCount, err := wire.ReadVarInt(witnessReader, 0)
if err != nil {
return nil, err
}
// Now that we know how may inputs we'll need, we'll
// construct a packing slice, then read out each input
// (with a varint prefix) from the witnessReader.
tin.Witness = make(wire.TxWitness, witCount)
for j := uint64(0); j < witCount; j++ {
wit, err := wire.ReadVarBytes(
witnessReader, 0, txscript.MaxScriptSize, "witness",
)
if err != nil {
return nil, err
}
tin.Witness[j] = wit
}
}
}
return finalTx, nil
}

462
vendor/github.com/btcsuite/btcutil/psbt/finalizer.go generated vendored Normal file
View File

@ -0,0 +1,462 @@
// 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 psbt
// The Finalizer requires provision of a single PSBT input
// in which all necessary signatures are encoded, and
// uses it to construct valid final sigScript and scriptWitness
// fields.
// NOTE that p2sh (legacy) and p2wsh currently support only
// multisig and no other custom script.
import (
"github.com/btcsuite/btcd/txscript"
)
// isFinalized considers this input finalized if it contains at least one of
// the FinalScriptSig or FinalScriptWitness are filled (which only occurs in a
// successful call to Finalize*).
func isFinalized(p *Packet, inIndex int) bool {
input := p.Inputs[inIndex]
return input.FinalScriptSig != nil || input.FinalScriptWitness != nil
}
// isFinalizableWitnessInput returns true if the target input is a witness UTXO
// that can be finalized.
func isFinalizableWitnessInput(pInput *PInput) bool {
pkScript := pInput.WitnessUtxo.PkScript
switch {
// If this is a native witness output, then we require both
// the witness script, but not a redeem script.
case txscript.IsWitnessProgram(pkScript):
if txscript.IsPayToWitnessScriptHash(pkScript) {
if pInput.WitnessScript == nil ||
pInput.RedeemScript != nil {
return false
}
} else {
// A P2WKH output on the other hand doesn't need
// neither a witnessScript or redeemScript.
if pInput.WitnessScript != nil ||
pInput.RedeemScript != nil {
return false
}
}
// For nested P2SH inputs, we verify that a witness script is known.
case txscript.IsPayToScriptHash(pkScript):
if pInput.RedeemScript == nil {
return false
}
// If this is a nested P2SH input, then it must also have a
// witness script, while we don't need one for P2WKH.
if txscript.IsPayToWitnessScriptHash(pInput.RedeemScript) {
if pInput.WitnessScript == nil {
return false
}
} else if txscript.IsPayToWitnessPubKeyHash(pInput.RedeemScript) {
if pInput.WitnessScript != nil {
return false
}
} else {
// unrecognized type
return false
}
// If this isn't a nested nested P2SH output or a native witness
// output, then we can't finalize this input as we don't understand it.
default:
return false
}
return true
}
// isFinalizableLegacyInput returns true of the passed input a legacy input
// (non-witness) that can be finalized.
func isFinalizableLegacyInput(p *Packet, pInput *PInput, inIndex int) bool {
// If the input has a witness, then it's invalid.
if pInput.WitnessScript != nil {
return false
}
// Otherwise, we'll verify that we only have a RedeemScript if the prev
// output script is P2SH.
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
if txscript.IsPayToScriptHash(pInput.NonWitnessUtxo.TxOut[outIndex].PkScript) {
if pInput.RedeemScript == nil {
return false
}
} else {
if pInput.RedeemScript != nil {
return false
}
}
return true
}
// isFinalizable checks whether the structure of the entry for the input of the
// psbt.Packet at index inIndex contains sufficient information to finalize
// this input.
func isFinalizable(p *Packet, inIndex int) bool {
pInput := p.Inputs[inIndex]
// The input cannot be finalized without any signatures
if pInput.PartialSigs == nil {
return false
}
// For an input to be finalized, we'll one of two possible top-level
// UTXOs present. Each UTXO type has a distinct set of requirements to
// be considered finalized.
switch {
// A witness input must be either native P2WSH or nested P2SH with all
// relevant sigScript or witness data populated.
case pInput.WitnessUtxo != nil:
if !isFinalizableWitnessInput(&pInput) {
return false
}
case pInput.NonWitnessUtxo != nil:
if !isFinalizableLegacyInput(p, &pInput, inIndex) {
return false
}
// If neither a known UTXO type isn't present at all, then we'll
// return false as we need one of them.
default:
return false
}
return true
}
// MaybeFinalize attempts to finalize the input at index inIndex in the PSBT p,
// returning true with no error if it succeeds, OR if the input has already
// been finalized.
func MaybeFinalize(p *Packet, inIndex int) (bool, error) {
if isFinalized(p, inIndex) {
return true, nil
}
if !isFinalizable(p, inIndex) {
return false, ErrNotFinalizable
}
if err := Finalize(p, inIndex); err != nil {
return false, err
}
return true, nil
}
// MaybeFinalizeAll attempts to finalize all inputs of the psbt.Packet that are
// not already finalized, and returns an error if it fails to do so.
func MaybeFinalizeAll(p *Packet) error {
for i := range p.UnsignedTx.TxIn {
success, err := MaybeFinalize(p, i)
if err != nil || !success {
return err
}
}
return nil
}
// Finalize assumes that the provided psbt.Packet struct has all partial
// signatures and redeem scripts/witness scripts already prepared for the
// specified input, and so removes all temporary data and replaces them with
// completed sigScript and witness fields, which are stored in key-types 07 and
// 08. The witness/non-witness utxo fields in the inputs (key-types 00 and 01)
// are left intact as they may be needed for validation (?). If there is any
// invalid or incomplete data, an error is returned.
func Finalize(p *Packet, inIndex int) error {
pInput := p.Inputs[inIndex]
// Depending on the UTXO type, we either attempt to finalize it as a
// witness or legacy UTXO.
switch {
case pInput.WitnessUtxo != nil:
if err := finalizeWitnessInput(p, inIndex); err != nil {
return err
}
case pInput.NonWitnessUtxo != nil:
if err := finalizeNonWitnessInput(p, inIndex); err != nil {
return err
}
default:
return ErrInvalidPsbtFormat
}
// Before returning we sanity check the PSBT to ensure we don't extract
// an invalid transaction or produce an invalid intermediate state.
if err := p.SanityCheck(); err != nil {
return err
}
return nil
}
// checkFinalScriptSigWitness checks whether a given input in the psbt.Packet
// struct already has the fields 07 (FinalInScriptSig) or 08 (FinalInWitness).
// If so, it returns true. It does not modify the Psbt.
func checkFinalScriptSigWitness(p *Packet, inIndex int) bool {
pInput := p.Inputs[inIndex]
if pInput.FinalScriptSig != nil {
return true
}
if pInput.FinalScriptWitness != nil {
return true
}
return false
}
// finalizeNonWitnessInput attempts to create a PsbtInFinalScriptSig field for
// the input at index inIndex, and removes all other fields except for the UTXO
// field, for an input of type non-witness, or returns an error.
func finalizeNonWitnessInput(p *Packet, inIndex int) error {
// If this input has already been finalized, then we'll return an error
// as we can't proceed.
if checkFinalScriptSigWitness(p, inIndex) {
return ErrInputAlreadyFinalized
}
// Our goal here is to construct a sigScript given the pubkey,
// signature (keytype 02), of which there might be multiple, and the
// redeem script field (keytype 04) if present (note, it is not present
// for p2pkh type inputs).
var sigScript []byte
pInput := p.Inputs[inIndex]
containsRedeemScript := pInput.RedeemScript != nil
var (
pubKeys [][]byte
sigs [][]byte
)
for _, ps := range pInput.PartialSigs {
pubKeys = append(pubKeys, ps.PubKey)
sigOK := checkSigHashFlags(ps.Signature, &pInput)
if !sigOK {
return ErrInvalidSigHashFlags
}
sigs = append(sigs, ps.Signature)
}
// We have failed to identify at least 1 (sig, pub) pair in the PSBT,
// which indicates it was not ready to be finalized. As a result, we
// can't proceed.
if len(sigs) < 1 || len(pubKeys) < 1 {
return ErrNotFinalizable
}
// If this input doesn't need a redeem script (P2PKH), then we'll
// construct a simple sigScript that's just the signature then the
// pubkey (OP_CHECKSIG).
var err error
if !containsRedeemScript {
// At this point, we should only have a single signature and
// pubkey.
if len(sigs) != 1 || len(pubKeys) != 1 {
return ErrNotFinalizable
}
// In this case, our sigScript is just: <sig> <pubkey>.
builder := txscript.NewScriptBuilder()
builder.AddData(sigs[0]).AddData(pubKeys[0])
sigScript, err = builder.Script()
if err != nil {
return err
}
} else {
// This is assumed p2sh multisig Given redeemScript and pubKeys
// we can decide in what order signatures must be appended.
orderedSigs, err := extractKeyOrderFromScript(
pInput.RedeemScript, pubKeys, sigs,
)
if err != nil {
return err
}
// At this point, we assume that this is a mult-sig input, so
// we construct our sigScript which looks something like this
// (mind the extra element for the extra multi-sig pop):
// * <nil> <sigs...> <redeemScript>
//
// TODO(waxwing): the below is specific to the multisig case.
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_FALSE)
for _, os := range orderedSigs {
builder.AddData(os)
}
builder.AddData(pInput.RedeemScript)
sigScript, err = builder.Script()
if err != nil {
return err
}
}
// At this point, a sigScript has been constructed. Remove all fields
// other than non-witness utxo (00) and finaliscriptsig (07)
newInput := NewPsbtInput(pInput.NonWitnessUtxo, nil)
newInput.FinalScriptSig = sigScript
// Overwrite the entry in the input list at the correct index. Note
// that this removes all the other entries in the list for this input
// index.
p.Inputs[inIndex] = *newInput
return nil
}
// finalizeWitnessInput attempts to create PsbtInFinalScriptSig field and
// PsbtInFinalScriptWitness field for input at index inIndex, and removes all
// other fields except for the utxo field, for an input of type witness, or
// returns an error.
func finalizeWitnessInput(p *Packet, inIndex int) error {
// If this input has already been finalized, then we'll return an error
// as we can't proceed.
if checkFinalScriptSigWitness(p, inIndex) {
return ErrInputAlreadyFinalized
}
// Depending on the actual output type, we'll either populate a
// serializedWitness or a witness as well asa sigScript.
var (
sigScript []byte
serializedWitness []byte
)
pInput := p.Inputs[inIndex]
// First we'll validate and collect the pubkey+sig pairs from the set
// of partial signatures.
var (
pubKeys [][]byte
sigs [][]byte
)
for _, ps := range pInput.PartialSigs {
pubKeys = append(pubKeys, ps.PubKey)
sigOK := checkSigHashFlags(ps.Signature, &pInput)
if !sigOK {
return ErrInvalidSigHashFlags
}
sigs = append(sigs, ps.Signature)
}
// If at this point, we don't have any pubkey+sig pairs, then we bail
// as we can't proceed.
if len(sigs) == 0 || len(pubKeys) == 0 {
return ErrNotFinalizable
}
containsRedeemScript := pInput.RedeemScript != nil
cointainsWitnessScript := pInput.WitnessScript != nil
// If there's no redeem script, then we assume that this is native
// segwit input.
var err error
if !containsRedeemScript {
// If we have only a sigley pubkey+sig pair, and no witness
// script, then we assume this is a P2WKH input.
if len(pubKeys) == 1 && len(sigs) == 1 &&
!cointainsWitnessScript {
serializedWitness, err = writePKHWitness(
sigs[0], pubKeys[0],
)
if err != nil {
return err
}
} else {
// Otherwise, we must have a witnessScript field, so
// we'll generate a valid multi-sig witness.
//
// NOTE: We tacitly assume multisig.
//
// TODO(roasbeef): need to add custom finalize for
// non-multisig P2WSH outputs (HTLCs, delay outputs,
// etc).
if !cointainsWitnessScript {
return ErrNotFinalizable
}
serializedWitness, err = getMultisigScriptWitness(
pInput.WitnessScript, pubKeys, sigs,
)
if err != nil {
return err
}
}
} else {
// Otherwise, we assume that this is a p2wsh multi-sig output,
// which is nested in a p2sh, or a p2wkh nested in a p2sh.
//
// In this case, we'll take the redeem script (the witness
// program in this case), and push it on the stack within the
// sigScript.
builder := txscript.NewScriptBuilder()
builder.AddData(pInput.RedeemScript)
sigScript, err = builder.Script()
if err != nil {
return err
}
// If don't have a witness script, then we assume this is a
// nested p2wkh output.
if !cointainsWitnessScript {
// Assumed p2sh-p2wkh Here the witness is just (sig,
// pub) as for p2pkh case
if len(sigs) != 1 || len(pubKeys) != 1 {
return ErrNotFinalizable
}
serializedWitness, err = writePKHWitness(sigs[0], pubKeys[0])
if err != nil {
return err
}
} else {
// Otherwise, we assume that this is a p2wsh multi-sig,
// so we generate the proper witness.
serializedWitness, err = getMultisigScriptWitness(
pInput.WitnessScript, pubKeys, sigs,
)
if err != nil {
return err
}
}
}
// At this point, a witness has been constructed, and a sigScript (if
// nested; else it's []). Remove all fields other than witness utxo
// (01) and finalscriptsig (07), finalscriptwitness (08).
newInput := NewPsbtInput(nil, pInput.WitnessUtxo)
if len(sigScript) > 0 {
newInput.FinalScriptSig = sigScript
}
newInput.FinalScriptWitness = serializedWitness
// Finally, we overwrite the entry in the input list at the correct
// index.
p.Inputs[inIndex] = *newInput
return nil
}

9
vendor/github.com/btcsuite/btcutil/psbt/go.mod generated vendored Normal file
View File

@ -0,0 +1,9 @@
module github.com/btcsuite/btcutil/psbt
go 1.13
require (
github.com/btcsuite/btcd v0.20.1-beta
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d
github.com/davecgh/go-spew v1.1.1
)

36
vendor/github.com/btcsuite/btcutil/psbt/go.sum generated vendored Normal file
View File

@ -0,0 +1,36 @@
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422 h1:EqnrgSSg0SFWRlEZLExgjtuUR/IPnuQ6qw6nwRda4Uk=
github.com/btcsuite/btcutil v0.0.0-20191219182022-e17c9730c422/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44 h1:9lP3x0pW80sDI6t1UMSLA4to18W7R7imwAI/sWS9S8Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,367 @@
package psbt
import (
"bytes"
"encoding/binary"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// PInput is a struct encapsulating all the data that can be attached to any
// specific input of the PSBT.
type PInput struct {
NonWitnessUtxo *wire.MsgTx
WitnessUtxo *wire.TxOut
PartialSigs []*PartialSig
SighashType txscript.SigHashType
RedeemScript []byte
WitnessScript []byte
Bip32Derivation []*Bip32Derivation
FinalScriptSig []byte
FinalScriptWitness []byte
Unknowns []*Unknown
}
// NewPsbtInput creates an instance of PsbtInput given either a nonWitnessUtxo
// or a witnessUtxo.
//
// NOTE: Only one of the two arguments should be specified, with the other
// being `nil`; otherwise the created PsbtInput object will fail IsSane()
// checks and will not be usable.
func NewPsbtInput(nonWitnessUtxo *wire.MsgTx,
witnessUtxo *wire.TxOut) *PInput {
return &PInput{
NonWitnessUtxo: nonWitnessUtxo,
WitnessUtxo: witnessUtxo,
PartialSigs: []*PartialSig{},
SighashType: 0,
RedeemScript: nil,
WitnessScript: nil,
Bip32Derivation: []*Bip32Derivation{},
FinalScriptSig: nil,
FinalScriptWitness: nil,
Unknowns: nil,
}
}
// IsSane returns true only if there are no conflicting values in the Psbt
// PInput. It checks that witness and non-witness utxo entries do not both
// exist, and that witnessScript entries are only added to witness inputs.
func (pi *PInput) IsSane() bool {
if pi.NonWitnessUtxo != nil && pi.WitnessUtxo != nil {
return false
}
if pi.WitnessUtxo == nil && pi.WitnessScript != nil {
return false
}
if pi.WitnessUtxo == nil && pi.FinalScriptWitness != nil {
return false
}
return true
}
// deserialize attempts to deserialize a new PInput from the passed io.Reader.
func (pi *PInput) deserialize(r io.Reader) error {
for {
keyint, keydata, err := getKey(r)
if err != nil {
return err
}
if keyint == -1 {
// Reached separator byte
break
}
value, err := wire.ReadVarBytes(
r, 0, MaxPsbtValueLength, "PSBT value",
)
if err != nil {
return err
}
switch InputType(keyint) {
case NonWitnessUtxoType:
if pi.NonWitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
tx := wire.NewMsgTx(2)
err := tx.Deserialize(bytes.NewReader(value))
if err != nil {
return err
}
pi.NonWitnessUtxo = tx
case WitnessUtxoType:
if pi.WitnessUtxo != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
txout, err := readTxOut(value)
if err != nil {
return err
}
pi.WitnessUtxo = txout
case PartialSigType:
newPartialSig := PartialSig{
PubKey: keydata,
Signature: value,
}
if !newPartialSig.checkValid() {
return ErrInvalidPsbtFormat
}
// Duplicate keys are not allowed
for _, x := range pi.PartialSigs {
if bytes.Equal(x.PubKey, newPartialSig.PubKey) {
return ErrDuplicateKey
}
}
pi.PartialSigs = append(pi.PartialSigs, &newPartialSig)
case SighashType:
if pi.SighashType != 0 {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
// Bounds check on value here since the sighash type must be a
// 32-bit unsigned integer.
if len(value) != 4 {
return ErrInvalidKeydata
}
shtype := txscript.SigHashType(
binary.LittleEndian.Uint32(value),
)
pi.SighashType = shtype
case RedeemScriptInputType:
if pi.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.RedeemScript = value
case WitnessScriptInputType:
if pi.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.WitnessScript = value
case Bip32DerivationInputType:
if !validatePubkey(keydata) {
return ErrInvalidPsbtFormat
}
master, derivationPath, err := readBip32Derivation(value)
if err != nil {
return err
}
// Duplicate keys are not allowed
for _, x := range pi.Bip32Derivation {
if bytes.Equal(x.PubKey, keydata) {
return ErrDuplicateKey
}
}
pi.Bip32Derivation = append(
pi.Bip32Derivation,
&Bip32Derivation{
PubKey: keydata,
MasterKeyFingerprint: master,
Bip32Path: derivationPath,
},
)
case FinalScriptSigType:
if pi.FinalScriptSig != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptSig = value
case FinalScriptWitnessType:
if pi.FinalScriptWitness != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
pi.FinalScriptWitness = value
default:
// A fall through case for any proprietary types.
keyintanddata := []byte{byte(keyint)}
keyintanddata = append(keyintanddata, keydata...)
newUnknown := &Unknown{
Key: keyintanddata,
Value: value,
}
// Duplicate key+keydata are not allowed
for _, x := range pi.Unknowns {
if bytes.Equal(x.Key, newUnknown.Key) &&
bytes.Equal(x.Value, newUnknown.Value) {
return ErrDuplicateKey
}
}
pi.Unknowns = append(pi.Unknowns, newUnknown)
}
}
return nil
}
// serialize attempts to serialize the target PInput into the passed io.Writer.
func (pi *PInput) serialize(w io.Writer) error {
if !pi.IsSane() {
return ErrInvalidPsbtFormat
}
if pi.NonWitnessUtxo != nil {
var buf bytes.Buffer
err := pi.NonWitnessUtxo.Serialize(&buf)
if err != nil {
return err
}
err = serializeKVPairWithType(
w, uint8(NonWitnessUtxoType), nil, buf.Bytes(),
)
if err != nil {
return err
}
}
if pi.WitnessUtxo != nil {
var buf bytes.Buffer
err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo)
if err != nil {
return err
}
err = serializeKVPairWithType(
w, uint8(WitnessUtxoType), nil, buf.Bytes(),
)
if err != nil {
return err
}
}
if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil {
sort.Sort(PartialSigSorter(pi.PartialSigs))
for _, ps := range pi.PartialSigs {
err := serializeKVPairWithType(
w, uint8(PartialSigType), ps.PubKey,
ps.Signature,
)
if err != nil {
return err
}
}
if pi.SighashType != 0 {
var shtBytes [4]byte
binary.LittleEndian.PutUint32(
shtBytes[:], uint32(pi.SighashType),
)
err := serializeKVPairWithType(
w, uint8(SighashType), nil, shtBytes[:],
)
if err != nil {
return err
}
}
if pi.RedeemScript != nil {
err := serializeKVPairWithType(
w, uint8(RedeemScriptInputType), nil,
pi.RedeemScript,
)
if err != nil {
return err
}
}
if pi.WitnessScript != nil {
err := serializeKVPairWithType(
w, uint8(WitnessScriptInputType), nil,
pi.WitnessScript,
)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(pi.Bip32Derivation))
for _, kd := range pi.Bip32Derivation {
err := serializeKVPairWithType(
w,
uint8(Bip32DerivationInputType), kd.PubKey,
SerializeBIP32Derivation(
kd.MasterKeyFingerprint, kd.Bip32Path,
),
)
if err != nil {
return err
}
}
}
if pi.FinalScriptSig != nil {
err := serializeKVPairWithType(
w, uint8(FinalScriptSigType), nil, pi.FinalScriptSig,
)
if err != nil {
return err
}
}
if pi.FinalScriptWitness != nil {
err := serializeKVPairWithType(
w, uint8(FinalScriptWitnessType), nil, pi.FinalScriptWitness,
)
if err != nil {
return err
}
}
// Unknown is a special case; we don't have a key type, only a key and
// a value field
for _, kv := range pi.Unknowns {
err := serializeKVpair(w, kv.Key, kv.Value)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,139 @@
package psbt
import (
"bytes"
"io"
"sort"
"github.com/btcsuite/btcd/wire"
)
// POutput is a struct encapsulating all the data that can be attached
// to any specific output of the PSBT.
type POutput struct {
RedeemScript []byte
WitnessScript []byte
Bip32Derivation []*Bip32Derivation
}
// NewPsbtOutput creates an instance of PsbtOutput; the three parameters
// redeemScript, witnessScript and Bip32Derivation are all allowed to be
// `nil`.
func NewPsbtOutput(redeemScript []byte, witnessScript []byte,
bip32Derivation []*Bip32Derivation) *POutput {
return &POutput{
RedeemScript: redeemScript,
WitnessScript: witnessScript,
Bip32Derivation: bip32Derivation,
}
}
// deserialize attempts to recode a new POutput from the passed io.Reader.
func (po *POutput) deserialize(r io.Reader) error {
for {
keyint, keydata, err := getKey(r)
if err != nil {
return err
}
if keyint == -1 {
// Reached separator byte
break
}
value, err := wire.ReadVarBytes(
r, 0, MaxPsbtValueLength, "PSBT value",
)
if err != nil {
return err
}
switch OutputType(keyint) {
case RedeemScriptOutputType:
if po.RedeemScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.RedeemScript = value
case WitnessScriptOutputType:
if po.WitnessScript != nil {
return ErrDuplicateKey
}
if keydata != nil {
return ErrInvalidKeydata
}
po.WitnessScript = value
case Bip32DerivationOutputType:
if !validatePubkey(keydata) {
return ErrInvalidKeydata
}
master, derivationPath, err := readBip32Derivation(value)
if err != nil {
return err
}
// Duplicate keys are not allowed
for _, x := range po.Bip32Derivation {
if bytes.Equal(x.PubKey, keydata) {
return ErrDuplicateKey
}
}
po.Bip32Derivation = append(po.Bip32Derivation,
&Bip32Derivation{
PubKey: keydata,
MasterKeyFingerprint: master,
Bip32Path: derivationPath,
},
)
default:
// Unknown type is allowed for inputs but not outputs.
return ErrInvalidPsbtFormat
}
}
return nil
}
// serialize attempts to write out the target POutput into the passed
// io.Writer.
func (po *POutput) serialize(w io.Writer) error {
if po.RedeemScript != nil {
err := serializeKVPairWithType(
w, uint8(RedeemScriptOutputType), nil, po.RedeemScript,
)
if err != nil {
return err
}
}
if po.WitnessScript != nil {
err := serializeKVPairWithType(
w, uint8(WitnessScriptOutputType), nil, po.WitnessScript,
)
if err != nil {
return err
}
}
sort.Sort(Bip32Sorter(po.Bip32Derivation))
for _, kd := range po.Bip32Derivation {
err := serializeKVPairWithType(w,
uint8(Bip32DerivationOutputType),
kd.PubKey,
SerializeBIP32Derivation(
kd.MasterKeyFingerprint,
kd.Bip32Path,
),
)
if err != nil {
return err
}
}
return nil
}

58
vendor/github.com/btcsuite/btcutil/psbt/partialsig.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
package psbt
import (
"bytes"
"github.com/btcsuite/btcd/btcec"
)
// PartialSig encapsulate a (BTC public key, ECDSA signature)
// pair, note that the fields are stored as byte slices, not
// btcec.PublicKey or btcec.Signature (because manipulations will
// be with the former not the latter, here); compliance with consensus
// serialization is enforced with .checkValid()
type PartialSig struct {
PubKey []byte
Signature []byte
}
// PartialSigSorter implements sort.Interface for PartialSig.
type PartialSigSorter []*PartialSig
func (s PartialSigSorter) Len() int { return len(s) }
func (s PartialSigSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s PartialSigSorter) Less(i, j int) bool {
return bytes.Compare(s[i].PubKey, s[j].PubKey) < 0
}
// validatePubkey checks if pubKey is *any* valid pubKey serialization in a
// Bitcoin context (compressed/uncomp. OK).
func validatePubkey(pubKey []byte) bool {
_, err := btcec.ParsePubKey(pubKey, btcec.S256())
if err != nil {
return false
}
return true
}
// validateSignature checks that the passed byte slice is a valid DER-encoded
// ECDSA signature, including the sighash flag. It does *not* of course
// validate the signature against any message or public key.
func validateSignature(sig []byte) bool {
_, err := btcec.ParseDERSignature(sig, btcec.S256())
if err != nil {
return false
}
return true
}
// checkValid checks that both the pbukey and sig are valid. See the methods
// (PartialSig, validatePubkey, validateSignature) for more details.
//
// TODO(waxwing): update for Schnorr will be needed here if/when that
// activates.
func (ps *PartialSig) checkValid() bool {
return validatePubkey(ps.PubKey) && validateSignature(ps.Signature)
}

407
vendor/github.com/btcsuite/btcutil/psbt/psbt.go generated vendored Normal file
View File

@ -0,0 +1,407 @@
// 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 psbt is an implementation of Partially Signed Bitcoin
// Transactions (PSBT). The format is defined in BIP 174:
// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
package psbt
import (
"bytes"
"encoding/base64"
"errors"
"io"
"github.com/btcsuite/btcd/wire"
)
// psbtMagicLength is the length of the magic bytes used to signal the start of
// a serialized PSBT packet.
const psbtMagicLength = 5
var (
// psbtMagic is the separator
psbtMagic = [psbtMagicLength]byte{0x70,
0x73, 0x62, 0x74, 0xff, // = "psbt" + 0xff sep
}
)
// MaxPsbtValueLength is the size of the largest transaction serialization
// that could be passed in a NonWitnessUtxo field. This is definitely
//less than 4M.
const MaxPsbtValueLength = 4000000
// MaxPsbtKeyLength is the length of the largest key that we'll successfully
// deserialize from the wire. Anything more will return ErrInvalidKeydata.
const MaxPsbtKeyLength = 10000
var (
// ErrInvalidPsbtFormat is a generic error for any situation in which a
// provided Psbt serialization does not conform to the rules of BIP174.
ErrInvalidPsbtFormat = errors.New("Invalid PSBT serialization format")
// ErrDuplicateKey indicates that a passed Psbt serialization is invalid
// due to having the same key repeated in the same key-value pair.
ErrDuplicateKey = errors.New("Invalid Psbt due to duplicate key")
// ErrInvalidKeydata indicates that a key-value pair in the PSBT
// serialization contains data in the key which is not valid.
ErrInvalidKeydata = errors.New("Invalid key data")
// ErrInvalidMagicBytes indicates that a passed Psbt serialization is invalid
// due to having incorrect magic bytes.
ErrInvalidMagicBytes = errors.New("Invalid Psbt due to incorrect magic bytes")
// ErrInvalidRawTxSigned indicates that the raw serialized transaction in the
// global section of the passed Psbt serialization is invalid because it
// contains scriptSigs/witnesses (i.e. is fully or partially signed), which
// is not allowed by BIP174.
ErrInvalidRawTxSigned = errors.New("Invalid Psbt, raw transaction must " +
"be unsigned.")
// ErrInvalidPrevOutNonWitnessTransaction indicates that the transaction
// hash (i.e. SHA256^2) of the fully serialized previous transaction
// provided in the NonWitnessUtxo key-value field doesn't match the prevout
// hash in the UnsignedTx field in the PSBT itself.
ErrInvalidPrevOutNonWitnessTransaction = errors.New("Prevout hash does " +
"not match the provided non-witness utxo serialization")
// ErrInvalidSignatureForInput indicates that the signature the user is
// trying to append to the PSBT is invalid, either because it does
// not correspond to the previous transaction hash, or redeem script,
// or witness script.
// NOTE this does not include ECDSA signature checking.
ErrInvalidSignatureForInput = errors.New("Signature does not correspond " +
"to this input")
// ErrInputAlreadyFinalized indicates that the PSBT passed to a Finalizer
// already contains the finalized scriptSig or witness.
ErrInputAlreadyFinalized = errors.New("Cannot finalize PSBT, finalized " +
"scriptSig or scriptWitnes already exists")
// ErrIncompletePSBT indicates that the Extractor object
// was unable to successfully extract the passed Psbt struct because
// it is not complete
ErrIncompletePSBT = errors.New("PSBT cannot be extracted as it is " +
"incomplete")
// ErrNotFinalizable indicates that the PSBT struct does not have
// sufficient data (e.g. signatures) for finalization
ErrNotFinalizable = errors.New("PSBT is not finalizable")
// ErrInvalidSigHashFlags indicates that a signature added to the PSBT
// uses Sighash flags that are not in accordance with the requirement
// according to the entry in PsbtInSighashType, or otherwise not the
// default value (SIGHASH_ALL)
ErrInvalidSigHashFlags = errors.New("Invalid Sighash Flags")
// ErrUnsupportedScriptType indicates that the redeem script or
// scriptwitness given is not supported by this codebase, or is otherwise
// not valid.
ErrUnsupportedScriptType = errors.New("Unsupported script type")
)
// Unknown is a struct encapsulating a key-value pair for which the key type is
// unknown by this package; these fields are allowed in both the 'Global' and
// the 'Input' section of a PSBT.
type Unknown struct {
Key []byte
Value []byte
}
// Packet is the actual psbt repreesntation. It is a is a set of 1 + N + M
// key-value pair lists, 1 global, defining the unsigned transaction structure
// with N inputs and M outputs. These key-value pairs can contain scripts,
// signatures, key derivations and other transaction-defining data.
type Packet struct {
// UnsignedTx is the decoded unsigned transaction for this PSBT.
UnsignedTx *wire.MsgTx // Deserialization of unsigned tx
// Inputs contains all the information needed to properly sign this
// target input within the above transaction.
Inputs []PInput
// Outputs contains all information required to spend any outputs
// produced by this PSBT.
Outputs []POutput
// Unknowns are the set of custom types (global only) within this PSBT.
Unknowns []Unknown
}
// validateUnsignedTx returns true if the transaction is unsigned. Note that
// more basic sanity requirements, such as the presence of inputs and outputs,
// is implicitly checked in the call to MsgTx.Deserialize().
func validateUnsignedTX(tx *wire.MsgTx) bool {
for _, tin := range tx.TxIn {
if len(tin.SignatureScript) != 0 || len(tin.Witness) != 0 {
return false
}
}
return true
}
// NewFromUnsignedTx creates a new Psbt struct, without any signatures (i.e.
// only the global section is non-empty) using the passed unsigned transaction.
func NewFromUnsignedTx(tx *wire.MsgTx) (*Packet, error) {
if !validateUnsignedTX(tx) {
return nil, ErrInvalidRawTxSigned
}
inSlice := make([]PInput, len(tx.TxIn))
outSlice := make([]POutput, len(tx.TxOut))
unknownSlice := make([]Unknown, 0)
retPsbt := Packet{
UnsignedTx: tx,
Inputs: inSlice,
Outputs: outSlice,
Unknowns: unknownSlice,
}
return &retPsbt, nil
}
// NewFromRawBytes returns a new instance of a Packet struct created by reading
// from a byte slice. If the format is invalid, an error is returned. If the
// argument b64 is true, the passed byte slice is decoded from base64 encoding
// before processing.
//
// NOTE: To create a Packet from one's own data, rather than reading in a
// serialization from a counterparty, one should use a psbt.New.
func NewFromRawBytes(r io.Reader, b64 bool) (*Packet, error) {
// If the PSBT is encoded in bas64, then we'll create a new wrapper
// reader that'll allow us to incrementally decode the contents of the
// io.Reader.
if b64 {
based64EncodedReader := r
r = base64.NewDecoder(base64.StdEncoding, based64EncodedReader)
}
// The Packet struct does not store the fixed magic bytes, but they
// must be present or the serialization must be explicitly rejected.
var magic [5]byte
if _, err := io.ReadFull(r, magic[:]); err != nil {
return nil, err
}
if magic != psbtMagic {
return nil, ErrInvalidMagicBytes
}
// Next we parse the GLOBAL section. There is currently only 1 known
// key type, UnsignedTx. We insist this exists first; unknowns are
// allowed, but only after.
keyint, keydata, err := getKey(r)
if err != nil {
return nil, err
}
if GlobalType(keyint) != UnsignedTxType || keydata != nil {
return nil, ErrInvalidPsbtFormat
}
// Now that we've verified the global type is present, we'll decode it
// into a proper unsigned transaction, and validate it.
value, err := wire.ReadVarBytes(
r, 0, MaxPsbtValueLength, "PSBT value",
)
if err != nil {
return nil, err
}
msgTx := wire.NewMsgTx(2)
err = msgTx.Deserialize(bytes.NewReader(value))
if err != nil {
// If there are no inputs in this yet incomplete transaction,
// the wire package still incorrectly assumes it's encoded in
// the witness format. We can fix this by just trying the non-
// witness encoding too. If that also fails, it's probably an
// invalid transaction.
msgTx = wire.NewMsgTx(2)
err2 := msgTx.DeserializeNoWitness(bytes.NewReader(value))
// If the second attempt also failed, something else is wrong
// and it probably makes more sense to return the original
// error instead of the error from the workaround.
if err2 != nil {
return nil, err
}
}
if !validateUnsignedTX(msgTx) {
return nil, ErrInvalidRawTxSigned
}
// Next we parse any unknowns that may be present, making sure that we
// break at the separator.
var unknownSlice []Unknown
for {
keyint, keydata, err := getKey(r)
if err != nil {
return nil, ErrInvalidPsbtFormat
}
if keyint == -1 {
break
}
value, err := wire.ReadVarBytes(
r, 0, MaxPsbtValueLength, "PSBT value",
)
if err != nil {
return nil, err
}
keyintanddata := []byte{byte(keyint)}
keyintanddata = append(keyintanddata, keydata...)
newUnknown := Unknown{
Key: keyintanddata,
Value: value,
}
unknownSlice = append(unknownSlice, newUnknown)
}
// Next we parse the INPUT section.
inSlice := make([]PInput, len(msgTx.TxIn))
for i := range msgTx.TxIn {
input := PInput{}
err = input.deserialize(r)
if err != nil {
return nil, err
}
inSlice[i] = input
}
// Next we parse the OUTPUT section.
outSlice := make([]POutput, len(msgTx.TxOut))
for i := range msgTx.TxOut {
output := POutput{}
err = output.deserialize(r)
if err != nil {
return nil, err
}
outSlice[i] = output
}
// Populate the new Packet object
newPsbt := Packet{
UnsignedTx: msgTx,
Inputs: inSlice,
Outputs: outSlice,
Unknowns: unknownSlice,
}
// Extended sanity checking is applied here to make sure the
// externally-passed Packet follows all the rules.
if err = newPsbt.SanityCheck(); err != nil {
return nil, err
}
return &newPsbt, nil
}
// Serialize creates a binary serialization of the referenced Packet struct
// with lexicographical ordering (by key) of the subsections.
func (p *Packet) Serialize(w io.Writer) error {
// First we write out the precise set of magic bytes that identify a
// valid PSBT transaction.
if _, err := w.Write(psbtMagic[:]); err != nil {
return err
}
// Next we prep to write out the unsigned transaction by first
// serializing it into an intermediate buffer.
serializedTx := bytes.NewBuffer(
make([]byte, 0, p.UnsignedTx.SerializeSize()),
)
if err := p.UnsignedTx.Serialize(serializedTx); err != nil {
return err
}
// Now that we have the serialized transaction, we'll write it out to
// the proper global type.
err := serializeKVPairWithType(
w, uint8(UnsignedTxType), nil, serializedTx.Bytes(),
)
if err != nil {
return err
}
// With that our global section is done, so we'll write out the
// separator.
separator := []byte{0x00}
if _, err := w.Write(separator); err != nil {
return err
}
for _, pInput := range p.Inputs {
err := pInput.serialize(w)
if err != nil {
return err
}
if _, err := w.Write(separator); err != nil {
return err
}
}
for _, pOutput := range p.Outputs {
err := pOutput.serialize(w)
if err != nil {
return err
}
if _, err := w.Write(separator); err != nil {
return err
}
}
return nil
}
// B64Encode returns the base64 encoding of the serialization of
// the current PSBT, or an error if the encoding fails.
func (p *Packet) B64Encode() (string, error) {
var b bytes.Buffer
if err := p.Serialize(&b); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
}
// IsComplete returns true only if all of the inputs are
// finalized; this is particularly important in that it decides
// whether the final extraction to a network serialized signed
// transaction will be possible.
func (p *Packet) IsComplete() bool {
for i := 0; i < len(p.UnsignedTx.TxIn); i++ {
if !isFinalized(p, i) {
return false
}
}
return true
}
// SanityCheck checks conditions on a PSBT to ensure that it obeys the
// rules of BIP174, and returns true if so, false if not.
func (p *Packet) SanityCheck() error {
if !validateUnsignedTX(p.UnsignedTx) {
return ErrInvalidRawTxSigned
}
for _, tin := range p.Inputs {
if !tin.IsSane() {
return ErrInvalidPsbtFormat
}
}
return nil
}

152
vendor/github.com/btcsuite/btcutil/psbt/signer.go generated vendored Normal file
View File

@ -0,0 +1,152 @@
// 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 psbt
// signer encapsulates the role 'Signer' as specified in BIP174; it controls
// the insertion of signatures; the Sign() function will attempt to insert
// signatures using Updater.addPartialSignature, after first ensuring the Psbt
// is in the correct state.
import (
"github.com/btcsuite/btcd/txscript"
)
// SignOutcome is a enum-like value that expresses the outcome of a call to the
// Sign method.
type SignOutcome int
const (
// SignSuccesful indicates that the partial signature was successfully
// attached.
SignSuccesful = 0
// SignFinalized indicates that this input is already finalized, so the provided
// signature was *not* attached
SignFinalized = 1
// SignInvalid indicates that the provided signature data was not valid. In this case
// an error will also be returned.
SignInvalid = -1
)
// Sign allows the caller to sign a PSBT at a particular input; they
// must provide a signature and a pubkey, both as byte slices; they can also
// optionally provide both witnessScript and/or redeemScript, otherwise these
// arguments must be set as nil (and in that case, they must already be present
// in the PSBT if required for signing to succeed).
//
// This serves as a wrapper around Updater.addPartialSignature; it ensures that
// the redeemScript and witnessScript are updated as needed (note that the
// Updater is allowed to add redeemScripts and witnessScripts independently,
// before signing), and ensures that the right form of utxo field
// (NonWitnessUtxo or WitnessUtxo) is included in the input so that signature
// insertion (and then finalization) can take place.
func (u *Updater) Sign(inIndex int, sig []byte, pubKey []byte,
redeemScript []byte, witnessScript []byte) (SignOutcome, error) {
if isFinalized(u.Upsbt, inIndex) {
return SignFinalized, nil
}
// Add the witnessScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
if witnessScript != nil {
err := u.AddInWitnessScript(witnessScript, inIndex)
if err != nil {
return SignInvalid, err
}
}
// Add the redeemScript to the PSBT in preparation. If it already
// exists, it will be overwritten.
if redeemScript != nil {
err := u.AddInRedeemScript(redeemScript, inIndex)
if err != nil {
return SignInvalid, err
}
}
// At this point, the PSBT must have the requisite witnessScript or
// redeemScript fields for signing to succeed.
//
// Case 1: if witnessScript is present, it must be of type witness;
// if not, signature insertion will of course fail.
switch {
case u.Upsbt.Inputs[inIndex].WitnessScript != nil:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return SignInvalid, err
}
}
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return SignInvalid, err
}
// Case 2: no witness script, only redeem script; can be legacy p2sh or
// p2sh-wrapped p2wkh.
case u.Upsbt.Inputs[inIndex].RedeemScript != nil:
// We only need to decide if the input is witness, and we don't
// rely on the witnessutxo/nonwitnessutxo in the PSBT, instead
// we check the redeemScript content.
if txscript.IsWitnessProgram(redeemScript) {
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return SignInvalid, err
}
}
}
// If it is not a valid witness program, we here assume that
// the provided WitnessUtxo/NonWitnessUtxo field was correct.
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return SignInvalid, err
}
// Case 3: Neither provided only works for native p2wkh, or non-segwit
// non-p2sh. To check if it's segwit, check the scriptPubKey of the
// output.
default:
if u.Upsbt.Inputs[inIndex].WitnessUtxo == nil {
outIndex := u.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
script := u.Upsbt.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex].PkScript
if txscript.IsWitnessProgram(script) {
err := nonWitnessToWitness(u.Upsbt, inIndex)
if err != nil {
return SignInvalid, err
}
}
}
err := u.addPartialSignature(inIndex, sig, pubKey)
if err != nil {
return SignInvalid, err
}
}
return SignSuccesful, nil
}
// nonWitnessToWitness extracts the TxOut from the existing NonWitnessUtxo
// field in the given PSBT input and sets it as type witness by replacing the
// NonWitnessUtxo field with a WitnessUtxo field. See
// https://github.com/bitcoin/bitcoin/pull/14197.
func nonWitnessToWitness(p *Packet, inIndex int) error {
outIndex := p.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
txout := p.Inputs[inIndex].NonWitnessUtxo.TxOut[outIndex]
// Remove the non-witness first, else sanity check will not pass:
p.Inputs[inIndex].NonWitnessUtxo = nil
u := Updater{
Upsbt: p,
}
return u.AddInWitnessUtxo(txout, inIndex)
}

149
vendor/github.com/btcsuite/btcutil/psbt/types.go generated vendored Normal file
View File

@ -0,0 +1,149 @@
package psbt
// GlobalType is the set of types that are used at the global scope level
// within the PSBT.
type GlobalType uint8
const (
// UnsignedTxType is the global scope key that houses the unsigned
// transaction of the PSBT. The value is a transaction in network
// serialization. The scriptSigs and witnesses for each input must be
// empty. The transaction must be in the old serialization format
// (without witnesses). A PSBT must have a transaction, otherwise it is
// invalid.
UnsignedTxType GlobalType = 0
// XpubType houses a global xpub for the entire PSBT packet.
//
// The key ({0x01}|{xpub}) is he 78 byte serialized extended public key
// as defined by BIP 32. Extended public keys are those that can be
// used to derive public keys used in the inputs and outputs of this
// transaction. It should be the public key at the highest hardened
// derivation index so that
// the unhardened child keys used in the transaction can be derived.
//
// The value is the master key fingerprint as defined by BIP 32
// concatenated with the derivation path of the public key. The
// derivation path is represented as 32-bit little endian unsigned
// integer indexes concatenated with each other. The number of 32 bit
// unsigned integer indexes must match the depth provided in the
// extended public key.
XpubType GlobalType = 1
// VersionType houses the global version number of this PSBT. There is
// no key (only contains the byte type), then the value if omitted, is
// assumed to be zero.
VersionType GlobalType = 0xFB
// ProprietaryGlobalType is used to house any proper chary global-scope
// keys within the PSBT.
//
// The key is ({0xFC}|<prefix>|{subtype}|{key data}) a variable length
// identifier prefix, followed by a subtype, followed by the key data
// itself.
//
// The value is any data as defined by the proprietary type user.
ProprietaryGlobalType = 0xFC
)
// InputType is the set of types that are defined for each input included
// within the PSBT.
type InputType uint32
const (
// NonWitnessUtxoType has no key ({0x00}) and houses the transaction in
// network serialization format the current input spends from. This
// should only be present for inputs which spend non-segwit outputs.
// However, if it is unknown whether an input spends a segwit output,
// this type should be used. The entire input transaction is needed in
// order to be able to verify the values of the input (pre-segwit they
// aren't in the signature digest).
NonWitnessUtxoType InputType = 0
// WitnessUtxoType has no key ({0x01}), and houses the entire
// transaction output in network serialization which the current input
// spends from. This should only be present for inputs which spend
// segwit outputs, including P2SH embedded ones (value || script).
WitnessUtxoType InputType = 1
// PartialSigType is used to include a partial signature with key
// ({0x02}|{public key}).
//
// The value is the signature as would be pushed to the stack from a
// scriptSig or witness..
PartialSigType InputType = 2
// SighashType is an empty key ({0x03}).
//
// The value contains the 32-bit unsigned integer specifying the
// sighash type to be used for this input. Signatures for this input
// must use the sighash type, finalizers must fail to finalize inputs
// which have signatures that do not match the specified sighash type.
// Signers who cannot produce signatures with the sighash type must not
// provide a signature.
SighashType InputType = 3
// RedeemScriptInputType is an empty key ({0x40}).
//
// The value is the redeem script of the input if present.
RedeemScriptInputType InputType = 4
// WitnessScriptInputType is an empty key ({0x05}).
//
// The value is the witness script of this input, if it has one.
WitnessScriptInputType InputType = 5
// Bip32DerivationInputType is a type that carries the pubkey along
// with the key ({0x06}|{public key}).
//
// The value is master key fingerprint as defined by BIP 32
// concatenated with the derivation path of the public key. The
// derivation path is represented as 32 bit unsigned integer indexes
// concatenated with each other. Public keys are those that will be
// needed to sign this input.
Bip32DerivationInputType InputType = 6
// FinalScriptSigType is an empty key ({0x07}).
//
// The value contains a fully constructed scriptSig with signatures and
// any other scripts necessary for the input to pass validation.
FinalScriptSigType InputType = 7
// FinalScriptWitnessType is an empty key ({0x08}). The value is a
// fully constructed scriptWitness with signatures and any other
// scripts necessary for the input to pass validation.
FinalScriptWitnessType InputType = 8
// ProprietaryInputType is a custom type for use by devs.
//
// The key ({0xFC}|<prefix>|{subtype}|{key data}), is a Variable length
// identifier prefix, followed by a subtype, followed by the key data
// itself.
//
// The value is any value data as defined by the proprietary type user.
ProprietaryInputType InputType = 0xFC
)
// OutputType is the set of types defined per output within the PSBT.
type OutputType uint32
const (
// RedeemScriptOutputType is an empty key ({0x00}>
//
// The value is the redeemScript for this output if it has one.
RedeemScriptOutputType OutputType = 0
// WitnessScriptOutputType is an empty key ({0x01}).
//
// The value is the witness script of this input, if it has one.
WitnessScriptOutputType OutputType = 1
j // Bip32DerivationOutputType is used to communicate derivation information
// needed to spend this output. The key is ({0x02}|{public key}).
//
// The value is master key fingerprint concatenated with the derivation
// path of the public key. The derivation path is represented as 32-bit
// little endian unsigned integer indexes concatenated with each other.
// Public keys are those needed to spend this output.
Bip32DerivationOutputType OutputType = 2
)

367
vendor/github.com/btcsuite/btcutil/psbt/updater.go generated vendored Normal file
View File

@ -0,0 +1,367 @@
// 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 psbt
// The Updater requires provision of a single PSBT and is able to add data to
// both input and output sections. It can be called repeatedly to add more
// data. It also allows addition of signatures via the addPartialSignature
// function; this is called internally to the package in the Sign() function of
// Updater, located in signer.go
import (
"bytes"
"crypto/sha256"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// Updater encapsulates the role 'Updater' as specified in BIP174; it accepts
// Psbt structs and has methods to add fields to the inputs and outputs.
type Updater struct {
Upsbt *Packet
}
// NewUpdater returns a new instance of Updater, if the passed Psbt struct is
// in a valid form, else an error.
func NewUpdater(p *Packet) (*Updater, error) {
if err := p.SanityCheck(); err != nil {
return nil, err
}
return &Updater{Upsbt: p}, nil
}
// AddInNonWitnessUtxo adds the utxo information for an input which is
// non-witness. This requires provision of a full transaction (which is the
// source of the corresponding prevOut), and the input index. If addition of
// this key-value pair to the Psbt fails, an error is returned.
func (p *Updater) AddInNonWitnessUtxo(tx *wire.MsgTx, inIndex int) error {
if inIndex > len(p.Upsbt.Inputs)-1 {
return ErrInvalidPrevOutNonWitnessTransaction
}
p.Upsbt.Inputs[inIndex].NonWitnessUtxo = tx
if err := p.Upsbt.SanityCheck(); err != nil {
return ErrInvalidPsbtFormat
}
return nil
}
// AddInWitnessUtxo adds the utxo information for an input which is witness.
// This requires provision of a full transaction *output* (which is the source
// of the corresponding prevOut); not the full transaction because BIP143 means
// the output information is sufficient, and the input index. If addition of
// this key-value pair to the Psbt fails, an error is returned.
func (p *Updater) AddInWitnessUtxo(txout *wire.TxOut, inIndex int) error {
if inIndex > len(p.Upsbt.Inputs)-1 {
return ErrInvalidPsbtFormat
}
p.Upsbt.Inputs[inIndex].WitnessUtxo = txout
if err := p.Upsbt.SanityCheck(); err != nil {
return ErrInvalidPsbtFormat
}
return nil
}
// addPartialSignature allows the Updater role to insert fields of type partial
// signature into a Psbt, consisting of both the pubkey (as keydata) and the
// ECDSA signature (as value). Note that the Signer role is encapsulated in
// this function; signatures are only allowed to be added that follow the
// sanity-check on signing rules explained in the BIP under `Signer`; if the
// rules are not satisfied, an ErrInvalidSignatureForInput is returned.
//
// NOTE: This function does *not* validate the ECDSA signature itself.
func (p *Updater) addPartialSignature(inIndex int, sig []byte,
pubkey []byte) error {
partialSig := PartialSig{
PubKey: pubkey, Signature: sig,
}
// First validate the passed (sig, pub).
if !partialSig.checkValid() {
return ErrInvalidPsbtFormat
}
pInput := p.Upsbt.Inputs[inIndex]
// First check; don't add duplicates.
for _, x := range pInput.PartialSigs {
if bytes.Equal(x.PubKey, partialSig.PubKey) {
return ErrDuplicateKey
}
}
// Next, we perform a series of additional sanity checks.
if pInput.NonWitnessUtxo != nil {
if len(p.Upsbt.UnsignedTx.TxIn) < inIndex+1 {
return ErrInvalidPrevOutNonWitnessTransaction
}
if pInput.NonWitnessUtxo.TxHash() !=
p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Hash {
return ErrInvalidSignatureForInput
}
// To validate that the redeem script matches, we must pull out
// the scriptPubKey of the corresponding output and compare
// that with the P2SH scriptPubKey that is generated by
// redeemScript.
if pInput.RedeemScript != nil {
outIndex := p.Upsbt.UnsignedTx.TxIn[inIndex].PreviousOutPoint.Index
scriptPubKey := pInput.NonWitnessUtxo.TxOut[outIndex].PkScript
scriptHash := btcutil.Hash160(pInput.RedeemScript)
scriptHashScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_HASH160).
AddData(scriptHash).
AddOp(txscript.OP_EQUAL).
Script()
if err != nil {
return err
}
if !bytes.Equal(scriptHashScript, scriptPubKey) {
return ErrInvalidSignatureForInput
}
}
} else if pInput.WitnessUtxo != nil {
scriptPubKey := pInput.WitnessUtxo.PkScript
var script []byte
if pInput.RedeemScript != nil {
scriptHash := btcutil.Hash160(pInput.RedeemScript)
scriptHashScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_HASH160).
AddData(scriptHash).
AddOp(txscript.OP_EQUAL).
Script()
if err != nil {
return err
}
if !bytes.Equal(scriptHashScript, scriptPubKey) {
return ErrInvalidSignatureForInput
}
script = pInput.RedeemScript
} else {
script = scriptPubKey
}
// If a witnessScript field is present, this is a P2WSH,
// whether nested or not (that is handled by the assignment to
// `script` above); in that case, sanity check that `script` is
// the p2wsh of witnessScript. Contrariwise, if no
// witnessScript field is present, this will be signed as
// p2wkh.
if pInput.WitnessScript != nil {
witnessScriptHash := sha256.Sum256(pInput.WitnessScript)
witnessScriptHashScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_0).
AddData(witnessScriptHash[:]).
Script()
if err != nil {
return err
}
if !bytes.Equal(script, witnessScriptHashScript[:]) {
return ErrInvalidSignatureForInput
}
} else {
// Otherwise, this is a p2wkh input.
pubkeyHash := btcutil.Hash160(pubkey)
pubkeyHashScript, err := txscript.NewScriptBuilder().
AddOp(txscript.OP_0).
AddData(pubkeyHash).
Script()
if err != nil {
return err
}
// Validate that we're able to properly reconstruct the
// witness program.
if !bytes.Equal(pubkeyHashScript, script) {
return ErrInvalidSignatureForInput
}
}
} else {
// Attaching signature without utxo field is not allowed.
return ErrInvalidPsbtFormat
}
p.Upsbt.Inputs[inIndex].PartialSigs = append(
p.Upsbt.Inputs[inIndex].PartialSigs, &partialSig,
)
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
// Addition of a non-duplicate-key partial signature cannot violate
// sanity-check rules.
return nil
}
// AddInSighashType adds the sighash type information for an input. The
// sighash type is passed as a 32 bit unsigned integer, along with the index
// for the input. An error is returned if addition of this key-value pair to
// the Psbt fails.
func (p *Updater) AddInSighashType(sighashType txscript.SigHashType,
inIndex int) error {
p.Upsbt.Inputs[inIndex].SighashType = sighashType
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
return nil
}
// AddInRedeemScript adds the redeem script information for an input. The
// redeem script is passed serialized, as a byte slice, along with the index of
// the input. An error is returned if addition of this key-value pair to the
// Psbt fails.
func (p *Updater) AddInRedeemScript(redeemScript []byte,
inIndex int) error {
p.Upsbt.Inputs[inIndex].RedeemScript = redeemScript
if err := p.Upsbt.SanityCheck(); err != nil {
return ErrInvalidPsbtFormat
}
return nil
}
// AddInWitnessScript adds the witness script information for an input. The
// witness script is passed serialized, as a byte slice, along with the index
// of the input. An error is returned if addition of this key-value pair to the
// Psbt fails.
func (p *Updater) AddInWitnessScript(witnessScript []byte,
inIndex int) error {
p.Upsbt.Inputs[inIndex].WitnessScript = witnessScript
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
return nil
}
// AddInBip32Derivation takes a master key fingerprint as defined in BIP32, a
// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte
// slice, along with the integer index of the input, and inserts this data into
// that input.
//
// NOTE: This can be called multiple times for the same input. An error is
// returned if addition of this key-value pair to the Psbt fails.
func (p *Updater) AddInBip32Derivation(masterKeyFingerprint uint32,
bip32Path []uint32, pubKeyData []byte, inIndex int) error {
bip32Derivation := Bip32Derivation{
PubKey: pubKeyData,
MasterKeyFingerprint: masterKeyFingerprint,
Bip32Path: bip32Path,
}
if !bip32Derivation.checkValid() {
return ErrInvalidPsbtFormat
}
// Don't allow duplicate keys
for _, x := range p.Upsbt.Inputs[inIndex].Bip32Derivation {
if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
return ErrDuplicateKey
}
}
p.Upsbt.Inputs[inIndex].Bip32Derivation = append(
p.Upsbt.Inputs[inIndex].Bip32Derivation, &bip32Derivation,
)
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
return nil
}
// AddOutBip32Derivation takes a master key fingerprint as defined in BIP32, a
// BIP32 path as a slice of uint32 values, and a serialized pubkey as a byte
// slice, along with the integer index of the output, and inserts this data
// into that output.
//
// NOTE: That this can be called multiple times for the same output. An error
// is returned if addition of this key-value pair to the Psbt fails.
func (p *Updater) AddOutBip32Derivation(masterKeyFingerprint uint32,
bip32Path []uint32, pubKeyData []byte, outIndex int) error {
bip32Derivation := Bip32Derivation{
PubKey: pubKeyData,
MasterKeyFingerprint: masterKeyFingerprint,
Bip32Path: bip32Path,
}
if !bip32Derivation.checkValid() {
return ErrInvalidPsbtFormat
}
// Don't allow duplicate keys
for _, x := range p.Upsbt.Outputs[outIndex].Bip32Derivation {
if bytes.Equal(x.PubKey, bip32Derivation.PubKey) {
return ErrDuplicateKey
}
}
p.Upsbt.Outputs[outIndex].Bip32Derivation = append(
p.Upsbt.Outputs[outIndex].Bip32Derivation, &bip32Derivation,
)
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
return nil
}
// AddOutRedeemScript takes a redeem script as a byte slice and appends it to
// the output at index outIndex.
func (p *Updater) AddOutRedeemScript(redeemScript []byte,
outIndex int) error {
p.Upsbt.Outputs[outIndex].RedeemScript = redeemScript
if err := p.Upsbt.SanityCheck(); err != nil {
return ErrInvalidPsbtFormat
}
return nil
}
// AddOutWitnessScript takes a witness script as a byte slice and appends it to
// the output at index outIndex.
func (p *Updater) AddOutWitnessScript(witnessScript []byte,
outIndex int) error {
p.Upsbt.Outputs[outIndex].WitnessScript = witnessScript
if err := p.Upsbt.SanityCheck(); err != nil {
return err
}
return nil
}

277
vendor/github.com/btcsuite/btcutil/psbt/utils.go generated vendored Normal file
View File

@ -0,0 +1,277 @@
// 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 psbt
import (
"bytes"
"encoding/binary"
"errors"
"io"
"sort"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
)
// writeTxWitness is a A utility function due to non-exported witness
// serialization (writeTxWitness encodes the bitcoin protocol encoding for a
// transaction input's witness into w).
func writeTxWitness(w io.Writer, wit [][]byte) error {
if err := wire.WriteVarInt(w, 0, uint64(len(wit))); err != nil {
return err
}
for _, item := range wit {
err := wire.WriteVarBytes(w, 0, item)
if err != nil {
return err
}
}
return nil
}
// writePKHWitness writes a witness for a p2wkh spending input
func writePKHWitness(sig []byte, pub []byte) ([]byte, error) {
var (
buf bytes.Buffer
witnessItems = [][]byte{sig, pub}
)
if err := writeTxWitness(&buf, witnessItems); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkIsMultisigScript is a utility function to check whether a given
// redeemscript fits the standard multisig template used in all P2SH based
// multisig, given a set of pubkeys for redemption.
func checkIsMultiSigScript(pubKeys [][]byte, sigs [][]byte,
script []byte) bool {
// First insist that the script type is multisig.
if txscript.GetScriptClass(script) != txscript.MultiSigTy {
return false
}
// Inspect the script to ensure that the number of sigs and pubkeys is
// correct
numSigs, numPubKeys, err := txscript.CalcMultiSigStats(script)
if err != nil {
return false
}
// If the number of sigs provided, doesn't match the number of required
// pubkeys, then we can't proceed as we're not yet final.
if numPubKeys != len(pubKeys) || numSigs != len(sigs) {
return false
}
return true
}
// extractKeyOrderFromScript is a utility function to extract an ordered list
// of signatures, given a serialized script (redeemscript or witness script), a
// list of pubkeys and the signatures corresponding to those pubkeys. This
// function is used to ensure that the signatures will be embedded in the final
// scriptSig or scriptWitness in the correct order.
func extractKeyOrderFromScript(script []byte, expectedPubkeys [][]byte,
sigs [][]byte) ([][]byte, error) {
// If this isn't a proper finalized multi-sig script, then we can't
// proceed.
if !checkIsMultiSigScript(expectedPubkeys, sigs, script) {
return nil, ErrUnsupportedScriptType
}
// Arrange the pubkeys and sigs into a slice of format:
// * [[pub,sig], [pub,sig],..]
type sigWithPub struct {
pubKey []byte
sig []byte
}
var pubsSigs []sigWithPub
for i, pub := range expectedPubkeys {
pubsSigs = append(pubsSigs, sigWithPub{
pubKey: pub,
sig: sigs[i],
})
}
// Now that we have the set of (pubkey, sig) pairs, we'll construct a
// position map that we can use to swap the order in the slice above to
// match how things are laid out in the script.
type positionEntry struct {
index int
value sigWithPub
}
var positionMap []positionEntry
// For each pubkey in our pubsSigs slice, we'll now construct a proper
// positionMap entry, based on _where_ in the script the pubkey first
// appears.
for _, p := range pubsSigs {
pos := bytes.Index(script, p.pubKey)
if pos < 0 {
return nil, errors.New("script does not contain pubkeys")
}
positionMap = append(positionMap, positionEntry{
index: pos,
value: p,
})
}
// Now that we have the position map full populated, we'll use the
// index data to properly sort the entries in the map based on where
// they appear in the script.
sort.Slice(positionMap, func(i, j int) bool {
return positionMap[i].index < positionMap[j].index
})
// Finally, we can simply iterate through the position map in order to
// extract the proper signature ordering.
sortedSigs := make([][]byte, 0, len(positionMap))
for _, x := range positionMap {
sortedSigs = append(sortedSigs, x.value.sig)
}
return sortedSigs, nil
}
// getMultisigScriptWitness creates a full psbt serialized Witness field for
// the transaction, given the public keys and signatures to be appended. This
// function will only accept witnessScripts of the type M of N multisig. This
// is used for both p2wsh and nested p2wsh multisig cases.
func getMultisigScriptWitness(witnessScript []byte, pubKeys [][]byte,
sigs [][]byte) ([]byte, error) {
// First using the script as a guide, we'll properly order the sigs
// according to how their corresponding pubkeys appear in the
// witnessScript.
orderedSigs, err := extractKeyOrderFromScript(
witnessScript, pubKeys, sigs,
)
if err != nil {
return nil, err
}
// Now that we know the proper order, we'll append each of the
// signatures into a new witness stack, then top it off with the
// witness script at the end, prepending the nil as we need the extra
// pop..
witnessElements := make(wire.TxWitness, 0, len(sigs)+2)
witnessElements = append(witnessElements, nil)
for _, os := range orderedSigs {
witnessElements = append(witnessElements, os)
}
witnessElements = append(witnessElements, witnessScript)
// Now that we have the full witness stack, we'll serialize it in the
// expected format, and return the final bytes.
var buf bytes.Buffer
if err = writeTxWitness(&buf, witnessElements); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// checkSigHashFlags compares the sighash flag byte on a signature with the
// value expected according to any PsbtInSighashType field in this section of
// the PSBT, and returns true if they match, false otherwise.
// If no SighashType field exists, it is assumed to be SIGHASH_ALL.
//
// TODO(waxwing): sighash type not restricted to one byte in future?
func checkSigHashFlags(sig []byte, input *PInput) bool {
expectedSighashType := txscript.SigHashAll
if input.SighashType != 0 {
expectedSighashType = input.SighashType
}
return expectedSighashType == txscript.SigHashType(sig[len(sig)-1])
}
// serializeKVpair writes out a kv pair using a varbyte prefix for each.
func serializeKVpair(w io.Writer, key []byte, value []byte) error {
if err := wire.WriteVarBytes(w, 0, key); err != nil {
return err
}
return wire.WriteVarBytes(w, 0, value)
}
// serializeKVPairWithType writes out to the passed writer a type coupled with
// a key.
func serializeKVPairWithType(w io.Writer, kt uint8, keydata []byte,
value []byte) error {
// If the key has no data, then we write a blank slice.
if keydata == nil {
keydata = []byte{}
}
// The final key to be written is: {type} || {keyData}
serializedKey := append([]byte{byte(kt)}, keydata...)
return serializeKVpair(w, serializedKey, value)
}
// getKey retrieves a single key - both the key type and the keydata (if
// present) from the stream and returns the key type as an integer, or -1 if
// the key was of zero length. This integer is is used to indicate the presence
// of a separator byte which indicates the end of a given key-value pair list,
// and the keydata as a byte slice or nil if none is present.
func getKey(r io.Reader) (int, []byte, error) {
// For the key, we read the varint separately, instead of using the
// available ReadVarBytes, because we have a specific treatment of 0x00
// here:
count, err := wire.ReadVarInt(r, 0)
if err != nil {
return -1, nil, ErrInvalidPsbtFormat
}
if count == 0 {
// A separator indicates end of key-value pair list.
return -1, nil, nil
}
// Check that we don't attempt to decode a dangerously large key.
if count > MaxPsbtKeyLength {
return -1, nil, ErrInvalidKeydata
}
// Next, we ready out the designated number of bytes, which may include
// a type, key, and optional data.
keyTypeAndData := make([]byte, count)
if _, err := io.ReadFull(r, keyTypeAndData[:]); err != nil {
return -1, nil, err
}
keyType := int(string(keyTypeAndData)[0])
// Note that the second return value will usually be empty, since most
// keys contain no more than the key type byte.
if len(keyTypeAndData) == 1 {
return keyType, nil, nil
}
// Otherwise, we return the key, along with any data that it may
// contain.
return keyType, keyTypeAndData[1:], nil
}
// readTxOut is a limited version of wire.ReadTxOut, because the latter is not
// exported.
func readTxOut(txout []byte) (*wire.TxOut, error) {
if len(txout) < 10 {
return nil, ErrInvalidPsbtFormat
}
valueSer := binary.LittleEndian.Uint64(txout[:8])
scriptPubKey := txout[9:]
return wire.NewTxOut(int64(valueSer), scriptPubKey), nil
}

31
vendor/github.com/btcsuite/btcutil/txsort/README.md generated vendored Normal file
View File

@ -0,0 +1,31 @@
txsort
======
[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](http://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/txsort)
Package txsort provides the transaction sorting according to [BIP 69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki).
BIP 69 defines a standard lexicographical sort order of transaction inputs and
outputs. This is useful to standardize transactions for faster multi-party
agreement as well as preventing information leaks in a single-party use case.
The BIP goes into more detail, but for a quick and simplistic overview, the
order for inputs is defined as first sorting on the previous output hash and
then on the index as a tie breaker. The order for outputs is defined as first
sorting on the amount and then on the raw public key script bytes as a tie
breaker.
A comprehensive suite of tests is provided to ensure proper functionality.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcutil/txsort
```
## License
Package txsort is licensed under the [copyfree](http://copyfree.org) ISC
License.

20
vendor/github.com/btcsuite/btcutil/txsort/doc.go generated vendored Normal file
View File

@ -0,0 +1,20 @@
// 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 txsort provides the transaction sorting according to BIP 69.
Overview
BIP 69 defines a standard lexicographical sort order of transaction inputs and
outputs. This is useful to standardize transactions for faster multi-party
agreement as well as preventing information leaks in a single-party use case.
The BIP goes into more detail, but for a quick and simplistic overview, the
order for inputs is defined as first sorting on the previous output hash and
then on the index as a tie breaker. The order for outputs is defined as first
sorting on the amount and then on the raw public key script bytes as a tie
breaker.
*/
package txsort

95
vendor/github.com/btcsuite/btcutil/txsort/txsort.go generated vendored Normal file
View File

@ -0,0 +1,95 @@
// 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.
// Provides functions for sorting tx inputs and outputs according to BIP 69
// (https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki)
package txsort
import (
"bytes"
"sort"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
)
// InPlaceSort modifies the passed transaction inputs and outputs to be sorted
// based on BIP 69.
//
// WARNING: This function must NOT be called with published transactions since
// it will mutate the transaction if it's not already sorted. This can cause
// issues if you mutate a tx in a block, for example, which would invalidate the
// block. It could also cause cached hashes, such as in a btcutil.Tx to become
// invalidated.
//
// The function should only be used if the caller is creating the transaction or
// is otherwise 100% positive mutating will not cause adverse affects due to
// other dependencies.
func InPlaceSort(tx *wire.MsgTx) {
sort.Sort(sortableInputSlice(tx.TxIn))
sort.Sort(sortableOutputSlice(tx.TxOut))
}
// Sort returns a new transaction with the inputs and outputs sorted based on
// BIP 69. The passed transaction is not modified and the new transaction
// might have a different hash if any sorting was done.
func Sort(tx *wire.MsgTx) *wire.MsgTx {
txCopy := tx.Copy()
sort.Sort(sortableInputSlice(txCopy.TxIn))
sort.Sort(sortableOutputSlice(txCopy.TxOut))
return txCopy
}
// IsSorted checks whether tx has inputs and outputs sorted according to BIP
// 69.
func IsSorted(tx *wire.MsgTx) bool {
if !sort.IsSorted(sortableInputSlice(tx.TxIn)) {
return false
}
if !sort.IsSorted(sortableOutputSlice(tx.TxOut)) {
return false
}
return true
}
type sortableInputSlice []*wire.TxIn
type sortableOutputSlice []*wire.TxOut
// For SortableInputSlice and SortableOutputSlice, three functions are needed
// to make it sortable with sort.Sort() -- Len, Less, and Swap
// Len and Swap are trivial. Less is BIP 69 specific.
func (s sortableInputSlice) Len() int { return len(s) }
func (s sortableOutputSlice) Len() int { return len(s) }
func (s sortableOutputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s sortableInputSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Input comparison function.
// First sort based on input hash (reversed / rpc-style), then index.
func (s sortableInputSlice) Less(i, j int) bool {
// Input hashes are the same, so compare the index.
ihash := s[i].PreviousOutPoint.Hash
jhash := s[j].PreviousOutPoint.Hash
if ihash == jhash {
return s[i].PreviousOutPoint.Index < s[j].PreviousOutPoint.Index
}
// At this point, the hashes are not equal, so reverse them to
// big-endian and return the result of the comparison.
const hashSize = chainhash.HashSize
for b := 0; b < hashSize/2; b++ {
ihash[b], ihash[hashSize-1-b] = ihash[hashSize-1-b], ihash[b]
jhash[b], jhash[hashSize-1-b] = jhash[hashSize-1-b], jhash[b]
}
return bytes.Compare(ihash[:], jhash[:]) == -1
}
// Output comparison function.
// First sort based on amount (smallest first), then PkScript.
func (s sortableOutputSlice) Less(i, j int) bool {
if s[i].Value == s[j].Value {
return bytes.Compare(s[i].PkScript, s[j].PkScript) < 0
}
return s[i].Value < s[j].Value
}

View File

@ -16,6 +16,28 @@ import (
"github.com/lightninglabs/gozmq"
)
const (
// rawBlockZMQCommand is the command used to receive raw block
// notifications from bitcoind through ZMQ.
rawBlockZMQCommand = "rawblock"
// rawTxZMQCommand is the command used to receive raw transaction
// notifications from bitcoind through ZMQ.
rawTxZMQCommand = "rawtx"
// maxRawBlockSize is the maximum size in bytes for a raw block received
// from bitcoind through ZMQ.
maxRawBlockSize = 4e6
// maxRawTxSize is the maximum size in bytes for a raw transaction
// received from bitcoind through ZMQ.
maxRawTxSize = maxRawBlockSize
// seqNumLen is the length of the sequence number of a message sent from
// bitcoind through ZMQ.
seqNumLen = 4
)
// BitcoindConn represents a persistent client connection to a bitcoind node
// that listens for events read from a ZMQ connection.
type BitcoindConn struct {
@ -79,7 +101,7 @@ func NewBitcoindConn(chainParams *chaincfg.Params,
// concern to ensure one type of event isn't dropped from the connection
// queue due to another type of event filling it up.
zmqBlockConn, err := gozmq.Subscribe(
zmqBlockHost, []string{"rawblock"}, zmqPollInterval,
zmqBlockHost, []string{rawBlockZMQCommand}, zmqPollInterval,
)
if err != nil {
return nil, fmt.Errorf("unable to subscribe for zmq block "+
@ -87,7 +109,7 @@ func NewBitcoindConn(chainParams *chaincfg.Params,
}
zmqTxConn, err := gozmq.Subscribe(
zmqTxHost, []string{"rawtx"}, zmqPollInterval,
zmqTxHost, []string{rawTxZMQCommand}, zmqPollInterval,
)
if err != nil {
zmqBlockConn.Close()
@ -164,6 +186,20 @@ func (c *BitcoindConn) blockEventHandler() {
log.Info("Started listening for bitcoind block notifications via ZMQ "+
"on", c.zmqBlockConn.RemoteAddr())
// Set up the buffers we expect our messages to consume. ZMQ
// messages from bitcoind include three parts: the command, the
// data, and the sequence number.
//
// We'll allocate a fixed data slice that we'll reuse when reading
// blocks from bitcoind through ZMQ. There's no need to recycle this
// slice (zero out) after using it, as further reads will overwrite the
// slice and we'll only be deserializing the bytes needed.
var (
command [len(rawBlockZMQCommand)]byte
seqNum [seqNumLen]byte
data = make([]byte, maxRawBlockSize)
)
for {
// Before attempting to read from the ZMQ socket, we'll make
// sure to check if we've been requested to shut down.
@ -174,7 +210,11 @@ func (c *BitcoindConn) blockEventHandler() {
}
// Poll an event from the ZMQ socket.
msgBytes, err := c.zmqBlockConn.Receive()
var (
bufs = [][]byte{command[:], data, seqNum[:]}
err error
)
bufs, err = c.zmqBlockConn.Receive(bufs)
if err != nil {
// EOF should only be returned if the connection was
// explicitly closed, so we can exit at this point.
@ -187,22 +227,24 @@ func (c *BitcoindConn) blockEventHandler() {
// error to prevent spamming the logs.
netErr, ok := err.(net.Error)
if ok && netErr.Timeout() {
log.Trace("Re-establishing timed out ZMQ " +
"block connection")
continue
}
log.Errorf("Unable to receive ZMQ rawblock message: %v",
err)
log.Errorf("Unable to receive ZMQ %v message: %v",
rawBlockZMQCommand, err)
continue
}
// We have an event! We'll now ensure it is a block event,
// deserialize it, and report it to the different rescan
// clients.
eventType := string(msgBytes[0])
eventType := string(bufs[0])
switch eventType {
case "rawblock":
case rawBlockZMQCommand:
block := &wire.MsgBlock{}
r := bytes.NewReader(msgBytes[1])
r := bytes.NewReader(bufs[1])
if err := block.Deserialize(r); err != nil {
log.Errorf("Unable to deserialize block: %v",
err)
@ -229,8 +271,9 @@ func (c *BitcoindConn) blockEventHandler() {
continue
}
log.Warnf("Received unexpected event type from "+
"rawblock subscription: %v", eventType)
log.Warnf("Received unexpected event type from %v "+
"subscription: %v", rawBlockZMQCommand,
eventType)
}
}
}
@ -245,6 +288,20 @@ func (c *BitcoindConn) txEventHandler() {
log.Info("Started listening for bitcoind transaction notifications "+
"via ZMQ on", c.zmqTxConn.RemoteAddr())
// Set up the buffers we expect our messages to consume. ZMQ
// messages from bitcoind include three parts: the command, the
// data, and the sequence number.
//
// We'll allocate a fixed data slice that we'll reuse when reading
// transactions from bitcoind through ZMQ. There's no need to recycle
// this slice (zero out) after using it, as further reads will overwrite
// the slice and we'll only be deserializing the bytes needed.
var (
command [len(rawTxZMQCommand)]byte
seqNum [seqNumLen]byte
data = make([]byte, maxRawTxSize)
)
for {
// Before attempting to read from the ZMQ socket, we'll make
// sure to check if we've been requested to shut down.
@ -255,7 +312,11 @@ func (c *BitcoindConn) txEventHandler() {
}
// Poll an event from the ZMQ socket.
msgBytes, err := c.zmqTxConn.Receive()
var (
bufs = [][]byte{command[:], data, seqNum[:]}
err error
)
bufs, err = c.zmqTxConn.Receive(bufs)
if err != nil {
// EOF should only be returned if the connection was
// explicitly closed, so we can exit at this point.
@ -268,22 +329,24 @@ func (c *BitcoindConn) txEventHandler() {
// error to prevent spamming the logs.
netErr, ok := err.(net.Error)
if ok && netErr.Timeout() {
log.Trace("Re-establishing timed out ZMQ " +
"transaction connection")
continue
}
log.Errorf("Unable to receive ZMQ rawtx message: %v",
err)
log.Errorf("Unable to receive ZMQ %v message: %v",
rawTxZMQCommand, err)
continue
}
// We have an event! We'll now ensure it is a transaction event,
// deserialize it, and report it to the different rescan
// clients.
eventType := string(msgBytes[0])
eventType := string(bufs[0])
switch eventType {
case "rawtx":
case rawTxZMQCommand:
tx := &wire.MsgTx{}
r := bytes.NewReader(msgBytes[1])
r := bytes.NewReader(bufs[1])
if err := tx.Deserialize(r); err != nil {
log.Errorf("Unable to deserialize "+
"transaction: %v", err)
@ -310,8 +373,8 @@ func (c *BitcoindConn) txEventHandler() {
continue
}
log.Warnf("Received unexpected event type from rawtx "+
"subscription: %v", eventType)
log.Warnf("Received unexpected event type from %v "+
"subscription: %v", rawTxZMQCommand, eventType)
}
}
}

View File

@ -199,6 +199,11 @@ func (s *NeutrinoClient) FilterBlocks(
// the filter returns a positive match, the full block is then requested
// and scanned for addresses using the block filterer.
for i, blk := range req.Blocks {
// TODO(wilmer): Investigate why polling it still necessary
// here. While testing, I ran into a few instances where the
// filter was not retrieved, leading to a panic. This should not
// happen in most cases thanks to the query logic revamp within
// Neutrino, but it seems there's still an uncovered edge case.
filter, err := s.pollCFilter(&blk.Hash)
if err != nil {
return nil, err
@ -312,7 +317,9 @@ func (s *NeutrinoClient) pollCFilter(hash *chainhash.Hash) (*gcs.Filter, error)
time.Sleep(100 * time.Millisecond)
}
filter, err = s.CS.GetCFilter(*hash, wire.GCSFilterRegular)
filter, err = s.CS.GetCFilter(
*hash, wire.GCSFilterRegular, neutrino.OptimisticBatch(),
)
if err != nil {
count++
continue

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
// 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.
// +build !windows,!plan9
package rename
import (
"os"
)
// Atomic provides an atomic file rename. newpath is replaced if it
// already exists.
func Atomic(oldpath, newpath string) error {
return os.Rename(oldpath, newpath)
}

View File

@ -0,0 +1,71 @@
// The following is adapted from goleveldb
// (https://github.com/syndtr/goleveldb) under the following license:
//
// Copyright 2012 Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package rename
import (
"syscall"
"unsafe"
)
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procMoveFileExW = modkernel32.NewProc("MoveFileExW")
)
const (
_MOVEFILE_REPLACE_EXISTING = 1
)
func moveFileEx(from *uint16, to *uint16, flags uint32) error {
r1, _, e1 := syscall.Syscall(procMoveFileExW.Addr(), 3,
uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)),
uintptr(flags))
if r1 == 0 {
if e1 != 0 {
return error(e1)
} else {
return syscall.EINVAL
}
}
return nil
}
// Atomic provides an atomic file rename. newpath is replaced if it
// already exists.
func Atomic(oldpath, newpath string) error {
from, err := syscall.UTF16PtrFromString(oldpath)
if err != nil {
return err
}
to, err := syscall.UTF16PtrFromString(newpath)
if err != nil {
return err
}
return moveFileEx(from, to, _MOVEFILE_REPLACE_EXISTING)
}

View File

@ -0,0 +1,323 @@
// 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 prompt
import (
"bufio"
"bytes"
"encoding/hex"
"fmt"
"os"
"strings"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/internal/legacy/keystore"
"golang.org/x/crypto/ssh/terminal"
)
// ProvideSeed is used to prompt for the wallet seed which maybe required during
// upgrades.
func ProvideSeed() ([]byte, error) {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("Enter existing wallet seed: ")
seedStr, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
seedStr = strings.TrimSpace(strings.ToLower(seedStr))
seed, err := hex.DecodeString(seedStr)
if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
len(seed) > hdkeychain.MaxSeedBytes {
fmt.Printf("Invalid seed specified. Must be a "+
"hexadecimal value that is at least %d bits and "+
"at most %d bits\n", hdkeychain.MinSeedBytes*8,
hdkeychain.MaxSeedBytes*8)
continue
}
return seed, nil
}
}
// ProvidePrivPassphrase is used to prompt for the private passphrase which
// maybe required during upgrades.
func ProvidePrivPassphrase() ([]byte, error) {
prompt := "Enter the private passphrase of your wallet: "
for {
fmt.Print(prompt)
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
pass = bytes.TrimSpace(pass)
if len(pass) == 0 {
continue
}
return pass, nil
}
}
// promptList prompts the user with the given prefix, list of valid responses,
// and default list entry to use. The function will repeat the prompt to the
// user until they enter a valid response.
func promptList(reader *bufio.Reader, prefix string, validResponses []string, defaultEntry string) (string, error) {
// Setup the prompt according to the parameters.
validStrings := strings.Join(validResponses, "/")
var prompt string
if defaultEntry != "" {
prompt = fmt.Sprintf("%s (%s) [%s]: ", prefix, validStrings,
defaultEntry)
} else {
prompt = fmt.Sprintf("%s (%s): ", prefix, validStrings)
}
// Prompt the user until one of the valid responses is given.
for {
fmt.Print(prompt)
reply, err := reader.ReadString('\n')
if err != nil {
return "", err
}
reply = strings.TrimSpace(strings.ToLower(reply))
if reply == "" {
reply = defaultEntry
}
for _, validResponse := range validResponses {
if reply == validResponse {
return reply, nil
}
}
}
}
// promptListBool prompts the user for a boolean (yes/no) with the given prefix.
// The function will repeat the prompt to the user until they enter a valid
// reponse.
func promptListBool(reader *bufio.Reader, prefix string, defaultEntry string) (bool, error) {
// Setup the valid responses.
valid := []string{"n", "no", "y", "yes"}
response, err := promptList(reader, prefix, valid, defaultEntry)
if err != nil {
return false, err
}
return response == "yes" || response == "y", nil
}
// promptPass prompts the user for a passphrase with the given prefix. The
// function will ask the user to confirm the passphrase and will repeat the
// prompts until they enter a matching response.
func promptPass(reader *bufio.Reader, prefix string, confirm bool) ([]byte, error) {
// Prompt the user until they enter a passphrase.
prompt := fmt.Sprintf("%s: ", prefix)
for {
fmt.Print(prompt)
pass, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
pass = bytes.TrimSpace(pass)
if len(pass) == 0 {
continue
}
if !confirm {
return pass, nil
}
fmt.Print("Confirm passphrase: ")
confirm, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return nil, err
}
fmt.Print("\n")
confirm = bytes.TrimSpace(confirm)
if !bytes.Equal(pass, confirm) {
fmt.Println("The entered passphrases do not match")
continue
}
return pass, nil
}
}
// PrivatePass prompts the user for a private passphrase with varying behavior
// depending on whether the passed legacy keystore exists. When it does, the
// user is prompted for the existing passphrase which is then used to unlock it.
// On the other hand, when the legacy keystore is nil, the user is prompted for
// a new private passphrase. All prompts are repeated until the user enters a
// valid response.
func PrivatePass(reader *bufio.Reader, legacyKeyStore *keystore.Store) ([]byte, error) {
// When there is not an existing legacy wallet, simply prompt the user
// for a new private passphase and return it.
if legacyKeyStore == nil {
return promptPass(reader, "Enter the private "+
"passphrase for your new wallet", true)
}
// At this point, there is an existing legacy wallet, so prompt the user
// for the existing private passphrase and ensure it properly unlocks
// the legacy wallet so all of the addresses can later be imported.
fmt.Println("You have an existing legacy wallet. All addresses from " +
"your existing legacy wallet will be imported into the new " +
"wallet format.")
for {
privPass, err := promptPass(reader, "Enter the private "+
"passphrase for your existing wallet", false)
if err != nil {
return nil, err
}
// Keep prompting the user until the passphrase is correct.
if err := legacyKeyStore.Unlock([]byte(privPass)); err != nil {
if err == keystore.ErrWrongPassphrase {
fmt.Println(err)
continue
}
return nil, err
}
return privPass, nil
}
}
// PublicPass prompts the user whether they want to add an additional layer of
// encryption to the wallet. When the user answers yes and there is already a
// public passphrase provided via the passed config, it prompts them whether or
// not to use that configured passphrase. It will also detect when the same
// passphrase is used for the private and public passphrase and prompt the user
// if they are sure they want to use the same passphrase for both. Finally, all
// prompts are repeated until the user enters a valid response.
func PublicPass(reader *bufio.Reader, privPass []byte,
defaultPubPassphrase, configPubPassphrase []byte) ([]byte, error) {
pubPass := defaultPubPassphrase
usePubPass, err := promptListBool(reader, "Do you want "+
"to add an additional layer of encryption for public "+
"data?", "no")
if err != nil {
return nil, err
}
if !usePubPass {
return pubPass, nil
}
if !bytes.Equal(configPubPassphrase, pubPass) {
useExisting, err := promptListBool(reader, "Use the "+
"existing configured public passphrase for encryption "+
"of public data?", "no")
if err != nil {
return nil, err
}
if useExisting {
return configPubPassphrase, nil
}
}
for {
pubPass, err = promptPass(reader, "Enter the public "+
"passphrase for your new wallet", true)
if err != nil {
return nil, err
}
if bytes.Equal(pubPass, privPass) {
useSamePass, err := promptListBool(reader,
"Are you sure want to use the same passphrase "+
"for public and private data?", "no")
if err != nil {
return nil, err
}
if useSamePass {
break
}
continue
}
break
}
fmt.Println("NOTE: Use the --walletpass option to configure your " +
"public passphrase.")
return pubPass, nil
}
// Seed prompts the user whether they want to use an existing wallet generation
// seed. When the user answers no, a seed will be generated and displayed to
// the user along with prompting them for confirmation. When the user answers
// yes, a the user is prompted for it. All prompts are repeated until the user
// enters a valid response.
func Seed(reader *bufio.Reader) ([]byte, error) {
// Ascertain the wallet generation seed.
useUserSeed, err := promptListBool(reader, "Do you have an "+
"existing wallet seed you want to use?", "no")
if err != nil {
return nil, err
}
if !useUserSeed {
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
if err != nil {
return nil, err
}
fmt.Println("Your wallet generation seed is:")
fmt.Printf("%x\n", seed)
fmt.Println("IMPORTANT: Keep the seed in a safe place as you\n" +
"will NOT be able to restore your wallet without it.")
fmt.Println("Please keep in mind that anyone who has access\n" +
"to the seed can also restore your wallet thereby\n" +
"giving them access to all your funds, so it is\n" +
"imperative that you keep it in a secure location.")
for {
fmt.Print(`Once you have stored the seed in a safe ` +
`and secure location, enter "OK" to continue: `)
confirmSeed, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
confirmSeed = strings.TrimSpace(confirmSeed)
confirmSeed = strings.Trim(confirmSeed, `"`)
if confirmSeed == "OK" {
break
}
}
return seed, nil
}
for {
fmt.Print("Enter existing wallet seed: ")
seedStr, err := reader.ReadString('\n')
if err != nil {
return nil, err
}
seedStr = strings.TrimSpace(strings.ToLower(seedStr))
seed, err := hex.DecodeString(seedStr)
if err != nil || len(seed) < hdkeychain.MinSeedBytes ||
len(seed) > hdkeychain.MaxSeedBytes {
fmt.Printf("Invalid seed specified. Must be a "+
"hexadecimal value that is at least %d bits and "+
"at most %d bits\n", hdkeychain.MinSeedBytes*8,
hdkeychain.MaxSeedBytes*8)
continue
}
return seed, nil
}
}

View File

@ -32,8 +32,9 @@ report. Package waddrmgr is licensed under the liberal ISC license.
- Import WIF keys
- Import pay-to-script-hash scripts for things such as multi-signature
transactions
- Ability to export a watching-only version which does not contain any private
- Ability to start in watching-only mode which does not contain any private
key material
- Ability to convert to watching-only mode
- Programmatically detectable errors, including encapsulation of errors from
packages it relies on
- Address synchronization capabilities

View File

@ -850,6 +850,7 @@ func forEachAccount(ns walletdb.ReadBucket, scope *KeyScope,
}
// fetchLastAccount retrieves the last account from the database.
// If no accounts, returns twos-complement representation of -1, so that the next account is zero
func fetchLastAccount(ns walletdb.ReadBucket, scope *KeyScope) (uint32, error) {
scopedBucket, err := fetchReadScopeBucket(ns, scope)
if err != nil {
@ -859,6 +860,9 @@ func fetchLastAccount(ns walletdb.ReadBucket, scope *KeyScope) (uint32, error) {
metaBucket := scopedBucket.NestedReadBucket(metaBucketName)
val := metaBucket.Get(lastAccountName)
if val == nil {
return (1 << 32) - 1, nil
}
if len(val) != 4 {
str := fmt.Sprintf("malformed metadata '%s' stored in database",
lastAccountName)

View File

@ -123,6 +123,14 @@ var DefaultScryptOptions = ScryptOptions{
P: 1,
}
// FastScryptOptions are the scrypt options that should be used for testing
// purposes only where speed is more important than security.
var FastScryptOptions = ScryptOptions{
N: 16,
R: 8,
P: 1,
}
// addrKey is used to uniquely identify an address even when those addresses
// would end up being the same bitcoin address (as is the case for
// pay-to-pubkey and pay-to-pubkey-hash style of addresses).
@ -430,53 +438,61 @@ func (m *Manager) Close() {
//
// TODO(roasbeef): addrtype of raw key means it'll look in scripts to possibly
// mark as gucci?
func (m *Manager) NewScopedKeyManager(ns walletdb.ReadWriteBucket, scope KeyScope,
addrSchema ScopeAddrSchema) (*ScopedKeyManager, error) {
func (m *Manager) NewScopedKeyManager(ns walletdb.ReadWriteBucket,
scope KeyScope, addrSchema ScopeAddrSchema) (*ScopedKeyManager, error) {
m.mtx.Lock()
defer m.mtx.Unlock()
// If the manager is locked, then we can't create a new scoped manager.
if m.locked {
return nil, managerError(ErrLocked, errLocked, nil)
}
var rootPriv *hdkeychain.ExtendedKey
if !m.watchingOnly {
// If the manager is locked, then we can't create a new scoped
// manager.
if m.locked {
return nil, managerError(ErrLocked, errLocked, nil)
}
// Now that we know the manager is unlocked, we'll need to fetch the
// root master HD private key. This is required as we'll be attempting
// the following derivation: m/purpose'/cointype'
//
// Note that the path to the coin type is requires hardened derivation,
// therefore this can only be done if the wallet's root key hasn't been
// neutered.
masterRootPrivEnc, _, err := fetchMasterHDKeys(ns)
if err != nil {
return nil, err
}
// Now that we know the manager is unlocked, we'll need to
// fetch the root master HD private key. This is required as
// we'll be attempting the following derivation:
// m/purpose'/cointype'
//
// Note that the path to the coin type is requires hardened
// derivation, therefore this can only be done if the wallet's
// root key hasn't been neutered.
masterRootPrivEnc, _, err := fetchMasterHDKeys(ns)
if err != nil {
return nil, err
}
// If the master root private key isn't found within the database, but
// we need to bail here as we can't create the cointype key without the
// master root private key.
if masterRootPrivEnc == nil {
return nil, managerError(ErrWatchingOnly, "", nil)
}
// If the master root private key isn't found within the
// database, but we need to bail here as we can't create the
// cointype key without the master root private key.
if masterRootPrivEnc == nil {
return nil, managerError(ErrWatchingOnly, "", nil)
}
// Before we can derive any new scoped managers using this key, we'll
// need to fully decrypt it.
serializedMasterRootPriv, err := m.cryptoKeyPriv.Decrypt(masterRootPrivEnc)
if err != nil {
str := fmt.Sprintf("failed to decrypt master root serialized private key")
return nil, managerError(ErrLocked, str, err)
}
// Before we can derive any new scoped managers using this
// key, we'll need to fully decrypt it.
serializedMasterRootPriv, err :=
m.cryptoKeyPriv.Decrypt(masterRootPrivEnc)
if err != nil {
str := fmt.Sprintf("failed to decrypt master root " +
"serialized private key")
return nil, managerError(ErrLocked, str, err)
}
// Now that we know the root priv is within the database, we'll decode
// it into a usable object.
rootPriv, err := hdkeychain.NewKeyFromString(
string(serializedMasterRootPriv),
)
zero.Bytes(serializedMasterRootPriv)
if err != nil {
str := fmt.Sprintf("failed to create master extended private key")
return nil, managerError(ErrKeyChain, str, err)
// Now that we know the root priv is within the database,
// we'll decode it into a usable object.
rootPriv, err = hdkeychain.NewKeyFromString(
string(serializedMasterRootPriv),
)
zero.Bytes(serializedMasterRootPriv)
if err != nil {
str := fmt.Sprintf("failed to create master extended " +
"private key")
return nil, managerError(ErrKeyChain, str, err)
}
}
// Now that we have the root private key, we'll fetch the scope bucket
@ -498,19 +514,21 @@ func (m *Manager) NewScopedKeyManager(ns walletdb.ReadWriteBucket, scope KeyScop
}
scopeKey := scopeToBytes(&scope)
schemaBytes := scopeSchemaToBytes(&addrSchema)
err = scopeSchemas.Put(scopeKey[:], schemaBytes)
err := scopeSchemas.Put(scopeKey[:], schemaBytes)
if err != nil {
return nil, err
}
// With the database state created, we'll now derive the cointype key
// using the master HD private key, then encrypt it along with the
// first account using our crypto keys.
err = createManagerKeyScope(
ns, scope, rootPriv, m.cryptoKeyPub, m.cryptoKeyPriv,
)
if err != nil {
return nil, err
if !m.watchingOnly {
// With the database state created, we'll now derive the
// cointype key using the master HD private key, then encrypt
// it along with the first account using our crypto keys.
err = createManagerKeyScope(
ns, scope, rootPriv, m.cryptoKeyPub, m.cryptoKeyPriv,
)
if err != nil {
return nil, err
}
}
// Finally, we'll register this new scoped manager with the root
@ -730,6 +748,43 @@ func (m *Manager) ForEachActiveAddress(ns walletdb.ReadBucket, fn func(addr btcu
return nil
}
// ForEachRelevantActiveAddress invokes the given closure on each active
// address relevant to the wallet. Ideally, only addresses within the default
// key scopes would be relevant, but due to a bug (now fixed) in which change
// addresses could be created outside of the default key scopes, we now need to
// check for those as well.
func (m *Manager) ForEachRelevantActiveAddress(ns walletdb.ReadBucket,
fn func(addr btcutil.Address) error) error {
m.mtx.RLock()
defer m.mtx.RUnlock()
for _, scopedMgr := range m.scopedManagers {
// If the manager is for a default key scope, we'll return all
// addresses, otherwise we'll only return internal addresses, as
// that's the branch used for change addresses.
isDefaultKeyScope := false
for _, defaultKeyScope := range DefaultKeyScopes {
if scopedMgr.Scope() == defaultKeyScope {
isDefaultKeyScope = true
break
}
}
var err error
if isDefaultKeyScope {
err = scopedMgr.ForEachActiveAddress(ns, fn)
} else {
err = scopedMgr.ForEachInternalActiveAddress(ns, fn)
}
if err != nil {
return err
}
}
return nil
}
// ForEachAccountAddress calls the given function with each address of
// the given account stored in the manager, breaking early on error.
func (m *Manager) ForEachAccountAddress(ns walletdb.ReadBucket, account uint32,
@ -1253,7 +1308,7 @@ func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
masterKeyPriv *snacl.SecretKey, cryptoKeyPub EncryptorDecryptor,
cryptoKeyPrivEncrypted, cryptoKeyScriptEncrypted []byte, syncInfo *syncState,
birthday time.Time, privPassphraseSalt [saltSize]byte,
scopedManagers map[KeyScope]*ScopedKeyManager) *Manager {
scopedManagers map[KeyScope]*ScopedKeyManager, watchingOnly bool) *Manager {
m := &Manager{
chainParams: chainParams,
@ -1271,6 +1326,7 @@ func newManager(chainParams *chaincfg.Params, masterKeyPub *snacl.SecretKey,
scopedManagers: scopedManagers,
externalAddrSchemas: make(map[AddressType][]KeyScope),
internalAddrSchemas: make(map[AddressType][]KeyScope),
watchingOnly: watchingOnly,
}
for _, sMgr := range m.scopedManagers {
@ -1495,9 +1551,8 @@ func loadManager(ns walletdb.ReadBucket, pubPassphrase []byte,
mgr := newManager(
chainParams, &masterKeyPub, &masterKeyPriv,
cryptoKeyPub, cryptoKeyPrivEnc, cryptoKeyScriptEnc, syncInfo,
birthday, privPassphraseSalt, scopedManagers,
birthday, privPassphraseSalt, scopedManagers, watchingOnly,
)
mgr.watchingOnly = watchingOnly
for _, scopedManager := range scopedManagers {
scopedManager.rootManager = mgr
@ -1631,27 +1686,40 @@ func createManagerKeyScope(ns walletdb.ReadWriteBucket,
)
}
// Create creates a new address manager in the given namespace. The seed must
// conform to the standards described in hdkeychain.NewMaster and will be used
// to create the master root node from which all hierarchical deterministic
// addresses are derived. This allows all chained addresses in the address
// manager to be recovered by using the same seed.
// Create creates a new address manager in the given namespace.
//
// All private and public keys and information are protected by secret keys
// derived from the provided private and public passphrases. The public
// passphrase is required on subsequent opens of the address manager, and the
// private passphrase is required to unlock the address manager in order to
// gain access to any private keys and information.
// The seed must conform to the standards described in
// hdkeychain.NewMaster and will be used to create the master root
// node from which all hierarchical deterministic addresses are
// derived. This allows all chained addresses in the address manager
// to be recovered by using the same seed.
//
// If a config structure is passed to the function, that configuration will
// override the defaults.
// If the provided seed value is nil the address manager will be
// created in watchingOnly mode in which case no default accounts or
// scoped managers are created - it is up to the caller to create a
// new one with NewAccountWatchingOnly and NewScopedKeyManager.
//
// A ManagerError with an error code of ErrAlreadyExists will be returned the
// address manager already exists in the specified namespace.
func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []byte,
// All private and public keys and information are protected by secret
// keys derived from the provided private and public passphrases. The
// public passphrase is required on subsequent opens of the address
// manager, and the private passphrase is required to unlock the
// address manager in order to gain access to any private keys and
// information.
//
// If a config structure is passed to the function, that configuration
// will override the defaults.
//
// A ManagerError with an error code of ErrAlreadyExists will be
// returned the address manager already exists in the specified
// namespace.
func Create(ns walletdb.ReadWriteBucket,
seed, pubPassphrase, privPassphrase []byte,
chainParams *chaincfg.Params, config *ScryptOptions,
birthday time.Time) error {
// If the seed argument is nil we create in watchingOnly mode.
isWatchingOnly := seed == nil
// Return an error if the manager has already been created in
// the given database namespace.
exists := managerExists(ns)
@ -1660,13 +1728,17 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
}
// Ensure the private passphrase is not empty.
if len(privPassphrase) == 0 {
if !isWatchingOnly && len(privPassphrase) == 0 {
str := "private passphrase may not be empty"
return managerError(ErrEmptyPassphrase, str, nil)
}
// Perform the initial bucket creation and database namespace setup.
if err := createManagerNS(ns, ScopeAddrMap); err != nil {
defaultScopes := map[KeyScope]ScopeAddrSchema{}
if !isWatchingOnly {
defaultScopes = ScopeAddrMap
}
if err := createManagerNS(ns, defaultScopes); err != nil {
return maybeConvertDbError(err)
}
@ -1681,22 +1753,6 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
str := "failed to master public key"
return managerError(ErrCrypto, str, err)
}
masterKeyPriv, err := newSecretKey(&privPassphrase, config)
if err != nil {
str := "failed to master private key"
return managerError(ErrCrypto, str, err)
}
defer masterKeyPriv.Zero()
// Generate the private passphrase salt. This is used when hashing
// passwords to detect whether an unlock can be avoided when the manager
// is already unlocked.
var privPassphraseSalt [saltSize]byte
_, err = rand.Read(privPassphraseSalt[:])
if err != nil {
str := "failed to read random source for passphrase salt"
return managerError(ErrCrypto, str, err)
}
// Generate new crypto public, private, and script keys. These keys are
// used to protect the actual public and private data such as addresses,
@ -1706,18 +1762,6 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
str := "failed to generate crypto public key"
return managerError(ErrCrypto, str, err)
}
cryptoKeyPriv, err := newCryptoKey()
if err != nil {
str := "failed to generate crypto private key"
return managerError(ErrCrypto, str, err)
}
defer cryptoKeyPriv.Zero()
cryptoKeyScript, err := newCryptoKey()
if err != nil {
str := "failed to generate crypto script key"
return managerError(ErrCrypto, str, err)
}
defer cryptoKeyScript.Zero()
// Encrypt the crypto keys with the associated master keys.
cryptoKeyPubEnc, err := masterKeyPub.Encrypt(cryptoKeyPub.Bytes())
@ -1725,70 +1769,120 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
str := "failed to encrypt crypto public key"
return managerError(ErrCrypto, str, err)
}
cryptoKeyPrivEnc, err := masterKeyPriv.Encrypt(cryptoKeyPriv.Bytes())
if err != nil {
str := "failed to encrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
cryptoKeyScriptEnc, err := masterKeyPriv.Encrypt(cryptoKeyScript.Bytes())
if err != nil {
str := "failed to encrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
// Use the genesis block for the passed chain as the created at block
// for the default.
createdAt := &BlockStamp{Hash: *chainParams.GenesisHash, Height: 0}
createdAt := &BlockStamp{
Hash: *chainParams.GenesisHash,
Height: 0,
Timestamp: chainParams.GenesisBlock.Header.Timestamp,
}
// Create the initial sync state.
syncInfo := newSyncState(createdAt, createdAt)
// Save the master key params to the database.
pubParams := masterKeyPub.Marshal()
privParams := masterKeyPriv.Marshal()
err = putMasterKeyParams(ns, pubParams, privParams)
if err != nil {
return maybeConvertDbError(err)
}
// Generate the BIP0044 HD key structure to ensure the provided seed
// can generate the required structure with no issues.
var privParams []byte = nil
var masterKeyPriv *snacl.SecretKey
var cryptoKeyPrivEnc []byte = nil
var cryptoKeyScriptEnc []byte = nil
if !isWatchingOnly {
masterKeyPriv, err = newSecretKey(&privPassphrase, config)
if err != nil {
str := "failed to master private key"
return managerError(ErrCrypto, str, err)
}
defer masterKeyPriv.Zero()
// Derive the master extended key from the seed.
rootKey, err := hdkeychain.NewMaster(seed, chainParams)
if err != nil {
str := "failed to derive master extended key"
return managerError(ErrKeyChain, str, err)
}
rootPubKey, err := rootKey.Neuter()
if err != nil {
str := "failed to neuter master extended key"
return managerError(ErrKeyChain, str, err)
}
// Generate the private passphrase salt. This is used when
// hashing passwords to detect whether an unlock can be
// avoided when the manager is already unlocked.
var privPassphraseSalt [saltSize]byte
_, err = rand.Read(privPassphraseSalt[:])
if err != nil {
str := "failed to read random source for passphrase salt"
return managerError(ErrCrypto, str, err)
}
// Next, for each registers default manager scope, we'll create the
// hardened cointype key for it, as well as the first default account.
for _, defaultScope := range DefaultKeyScopes {
err := createManagerKeyScope(
ns, defaultScope, rootKey, cryptoKeyPub, cryptoKeyPriv,
)
cryptoKeyPriv, err := newCryptoKey()
if err != nil {
str := "failed to generate crypto private key"
return managerError(ErrCrypto, str, err)
}
defer cryptoKeyPriv.Zero()
cryptoKeyScript, err := newCryptoKey()
if err != nil {
str := "failed to generate crypto script key"
return managerError(ErrCrypto, str, err)
}
defer cryptoKeyScript.Zero()
cryptoKeyPrivEnc, err =
masterKeyPriv.Encrypt(cryptoKeyPriv.Bytes())
if err != nil {
str := "failed to encrypt crypto private key"
return managerError(ErrCrypto, str, err)
}
cryptoKeyScriptEnc, err =
masterKeyPriv.Encrypt(cryptoKeyScript.Bytes())
if err != nil {
str := "failed to encrypt crypto script key"
return managerError(ErrCrypto, str, err)
}
// Generate the BIP0044 HD key structure to ensure the
// provided seed can generate the required structure with no
// issues.
// Derive the master extended key from the seed.
rootKey, err := hdkeychain.NewMaster(seed, chainParams)
if err != nil {
str := "failed to derive master extended key"
return managerError(ErrKeyChain, str, err)
}
rootPubKey, err := rootKey.Neuter()
if err != nil {
str := "failed to neuter master extended key"
return managerError(ErrKeyChain, str, err)
}
// Next, for each registers default manager scope, we'll
// create the hardened cointype key for it, as well as the
// first default account.
for _, defaultScope := range DefaultKeyScopes {
err := createManagerKeyScope(
ns, defaultScope, rootKey, cryptoKeyPub, cryptoKeyPriv,
)
if err != nil {
return maybeConvertDbError(err)
}
}
// Before we proceed, we'll also store the root master private
// key within the database in an encrypted format. This is
// required as in the future, we may need to create additional
// scoped key managers.
masterHDPrivKeyEnc, err :=
cryptoKeyPriv.Encrypt([]byte(rootKey.String()))
if err != nil {
return maybeConvertDbError(err)
}
masterHDPubKeyEnc, err :=
cryptoKeyPub.Encrypt([]byte(rootPubKey.String()))
if err != nil {
return maybeConvertDbError(err)
}
err = putMasterHDKeys(ns, masterHDPrivKeyEnc, masterHDPubKeyEnc)
if err != nil {
return maybeConvertDbError(err)
}
privParams = masterKeyPriv.Marshal()
}
// Before we proceed, we'll also store the root master private key
// within the database in an encrypted format. This is required as in
// the future, we may need to create additional scoped key managers.
masterHDPrivKeyEnc, err := cryptoKeyPriv.Encrypt([]byte(rootKey.String()))
if err != nil {
return maybeConvertDbError(err)
}
masterHDPubKeyEnc, err := cryptoKeyPub.Encrypt([]byte(rootPubKey.String()))
if err != nil {
return maybeConvertDbError(err)
}
err = putMasterHDKeys(ns, masterHDPrivKeyEnc, masterHDPubKeyEnc)
// Save the master key params to the database.
err = putMasterKeyParams(ns, pubParams, privParams)
if err != nil {
return maybeConvertDbError(err)
}
@ -1800,9 +1894,9 @@ func Create(ns walletdb.ReadWriteBucket, seed, pubPassphrase, privPassphrase []b
return maybeConvertDbError(err)
}
// Save the fact this is not a watching-only address manager to the
// Save the watching-only mode of the address manager to the
// database.
err = putWatchingOnly(ns, false)
err = putWatchingOnly(ns, isWatchingOnly)
if err != nil {
return maybeConvertDbError(err)
}

View File

@ -1187,7 +1187,7 @@ func (s *ScopedKeyManager) LastInternalAddress(ns walletdb.ReadBucket,
}
// NewRawAccount creates a new account for the scoped manager. This method
// differs from the NewAccount method in that this method takes the acount
// differs from the NewAccount method in that this method takes the account
// number *directly*, rather than taking a string name for the account, then
// mapping that to the next highest account number.
func (s *ScopedKeyManager) NewRawAccount(ns walletdb.ReadWriteBucket, number uint32) error {
@ -1209,6 +1209,24 @@ func (s *ScopedKeyManager) NewRawAccount(ns walletdb.ReadWriteBucket, number uin
return s.newAccount(ns, number, name)
}
// NewRawAccountWatchingOnly creates a new watching only account for
// the scoped manager. This method differs from the
// NewAccountWatchingOnly method in that this method takes the account
// number *directly*, rather than taking a string name for the
// account, then mapping that to the next highest account number.
func (s *ScopedKeyManager) NewRawAccountWatchingOnly(
ns walletdb.ReadWriteBucket, number uint32,
pubKey *hdkeychain.ExtendedKey) error {
s.mtx.Lock()
defer s.mtx.Unlock()
// As this is an ad hoc account that may not follow our normal linear
// derivation, we'll create a new name for this account based off of
// the account number.
name := fmt.Sprintf("act:%v", number)
return s.newAccountWatchingOnly(ns, number, name, pubKey)
}
// NewAccount creates and returns a new account stored in the manager based on
// the given account name. If an account with the same name already exists,
// ErrDuplicateAccount will be returned. Since creating a new account requires
@ -1326,6 +1344,70 @@ func (s *ScopedKeyManager) newAccount(ns walletdb.ReadWriteBucket,
return putLastAccount(ns, &s.scope, account)
}
// NewAccountWatchingOnly is similar to NewAccount, but for watch-only wallets.
func (s *ScopedKeyManager) NewAccountWatchingOnly(ns walletdb.ReadWriteBucket, name string,
pubKey *hdkeychain.ExtendedKey) (uint32, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
// Fetch latest account, and create a new account in the same
// transaction Fetch the latest account number to generate the next
// account number
account, err := fetchLastAccount(ns, &s.scope)
if err != nil {
return 0, err
}
account++
// With the name validated, we'll create a new account for the new
// contiguous account.
if err := s.newAccountWatchingOnly(ns, account, name, pubKey); err != nil {
return 0, err
}
return account, nil
}
// newAccountWatchingOnly is similar to newAccount, but for watching-only wallets.
//
// NOTE: This function MUST be called with the manager lock held for writes.
func (s *ScopedKeyManager) newAccountWatchingOnly(ns walletdb.ReadWriteBucket, account uint32, name string,
pubKey *hdkeychain.ExtendedKey) error {
// Validate the account name.
if err := ValidateAccountName(name); err != nil {
return err
}
// Check that account with the same name does not exist
_, err := s.lookupAccount(ns, name)
if err == nil {
str := fmt.Sprintf("account with the same name already exists")
return managerError(ErrDuplicateAccount, str, err)
}
// Encrypt the default account keys with the associated crypto keys.
acctPubEnc, err := s.rootManager.cryptoKeyPub.Encrypt(
[]byte(pubKey.String()),
)
if err != nil {
str := "failed to encrypt public key for account"
return managerError(ErrCrypto, str, err)
}
// We have the encrypted account extended keys, so save them to the
// database
err = putAccountInfo(
ns, &s.scope, account, acctPubEnc, nil, 0, 0, name,
)
if err != nil {
return err
}
// Save last account metadata
return putLastAccount(ns, &s.scope, account)
}
// RenameAccount renames an account stored in the manager based on the given
// account number with the given name. If an account with the same name
// already exists, ErrDuplicateAccount will be returned.
@ -1700,6 +1782,7 @@ func (s *ScopedKeyManager) ForEachAccount(ns walletdb.ReadBucket,
}
// LastAccount returns the last account stored in the manager.
// If no accounts, returns twos-complement representation of -1
func (s *ScopedKeyManager) LastAccount(ns walletdb.ReadBucket) (uint32, error) {
return fetchLastAccount(ns, &s.scope)
}
@ -1760,3 +1843,30 @@ func (s *ScopedKeyManager) ForEachActiveAddress(ns walletdb.ReadBucket,
return nil
}
// ForEachInternalActiveAddress invokes the given closure on each _internal_
// active address belonging to the scoped key manager, breaking early on error.
func (s *ScopedKeyManager) ForEachInternalActiveAddress(ns walletdb.ReadBucket,
fn func(addr btcutil.Address) error) error {
s.mtx.Lock()
defer s.mtx.Unlock()
addrFn := func(rowInterface interface{}) error {
managedAddr, err := s.rowInterfaceToManaged(ns, rowInterface)
if err != nil {
return err
}
// Skip any non-internal branch addresses.
if !managedAddr.Internal() {
return nil
}
return fn(managedAddr.Address())
}
if err := forEachActiveAddress(ns, &s.scope, addrFn); err != nil {
return maybeConvertDbError(err)
}
return nil
}

31
vendor/github.com/btcsuite/btcwallet/wallet/README.md generated vendored Normal file
View File

@ -0,0 +1,31 @@
wallet
======
[![Build Status](https://travis-ci.org/btcsuite/btcwallet.png?branch=master)]
(https://travis-ci.org/btcsuite/btcwallet)
## Feature Overview
TODO: Flesh out this section
## Documentation
[![GoDoc](https://godoc.org/github.com/btcsuite/btcwallet/wallet?status.png)]
(http://godoc.org/github.com/btcsuite/btcwallet/wallet)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/btcsuite/btcwallet/wallet
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/btcsuite/btcwallet/wallet
## Installation
```bash
$ go get github.com/btcsuite/btcwallet/wallet
```
Package wallet is licensed under the [copyfree](http://copyfree.org) ISC
License.

View File

@ -0,0 +1,485 @@
// 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 wallet
import (
"bytes"
"fmt"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)
const (
// birthdayBlockDelta is the maximum time delta allowed between our
// birthday timestamp and our birthday block's timestamp when searching
// for a better birthday block candidate (if possible).
birthdayBlockDelta = 2 * time.Hour
)
func (w *Wallet) handleChainNotifications() {
defer w.wg.Done()
chainClient, err := w.requireChainClient()
if err != nil {
log.Errorf("handleChainNotifications called without RPC client")
return
}
catchUpHashes := func(w *Wallet, client chain.Interface,
height int32) error {
// TODO(aakselrod): There's a race conditon here, which
// happens when a reorg occurs between the
// rescanProgress notification and the last GetBlockHash
// call. The solution when using btcd is to make btcd
// send blockconnected notifications with each block
// the way Neutrino does, and get rid of the loop. The
// other alternative is to check the final hash and,
// if it doesn't match the original hash returned by
// the notification, to roll back and restart the
// rescan.
log.Infof("Catching up block hashes to height %d, this"+
" might take a while", height)
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
startBlock := w.Manager.SyncedTo()
for i := startBlock.Height + 1; i <= height; i++ {
hash, err := client.GetBlockHash(int64(i))
if err != nil {
return err
}
header, err := chainClient.GetBlockHeader(hash)
if err != nil {
return err
}
bs := waddrmgr.BlockStamp{
Height: i,
Hash: *hash,
Timestamp: header.Timestamp,
}
err = w.Manager.SetSyncedTo(ns, &bs)
if err != nil {
return err
}
}
return nil
})
if err != nil {
log.Errorf("Failed to update address manager "+
"sync state for height %d: %v", height, err)
}
log.Info("Done catching up block hashes")
return err
}
for {
select {
case n, ok := <-chainClient.Notifications():
if !ok {
return
}
var notificationName string
var err error
switch n := n.(type) {
case chain.ClientConnected:
// Before attempting to sync with our backend,
// we'll make sure that our birthday block has
// been set correctly to potentially prevent
// missing relevant events.
birthdayStore := &walletBirthdayStore{
db: w.db,
manager: w.Manager,
}
birthdayBlock, err := birthdaySanityCheck(
chainClient, birthdayStore,
)
if err != nil && !waddrmgr.IsError(err, waddrmgr.ErrBirthdayBlockNotSet) {
panic(fmt.Errorf("Unable to sanity "+
"check wallet birthday block: %v",
err))
}
err = w.syncWithChain(birthdayBlock)
if err != nil && !w.ShuttingDown() {
panic(fmt.Errorf("Unable to synchronize "+
"wallet to chain: %v", err))
}
case chain.BlockConnected:
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.connectBlock(tx, wtxmgr.BlockMeta(n))
})
notificationName = "block connected"
case chain.BlockDisconnected:
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.disconnectBlock(tx, wtxmgr.BlockMeta(n))
})
notificationName = "block disconnected"
case chain.RelevantTx:
err = walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
return w.addRelevantTx(tx, n.TxRecord, n.Block)
})
notificationName = "relevant transaction"
case chain.FilteredBlockConnected:
// Atomically update for the whole block.
if len(n.RelevantTxs) > 0 {
err = walletdb.Update(w.db, func(
tx walletdb.ReadWriteTx) error {
var err error
for _, rec := range n.RelevantTxs {
err = w.addRelevantTx(tx, rec,
n.Block)
if err != nil {
return err
}
}
return nil
})
}
notificationName = "filtered block connected"
// The following require some database maintenance, but also
// need to be reported to the wallet's rescan goroutine.
case *chain.RescanProgress:
err = catchUpHashes(w, chainClient, n.Height)
notificationName = "rescan progress"
select {
case w.rescanNotifications <- n:
case <-w.quitChan():
return
}
case *chain.RescanFinished:
err = catchUpHashes(w, chainClient, n.Height)
notificationName = "rescan finished"
w.SetChainSynced(true)
select {
case w.rescanNotifications <- n:
case <-w.quitChan():
return
}
}
if err != nil {
// If we received a block connected notification
// while rescanning, then we can ignore logging
// the error as we'll properly catch up once we
// process the RescanFinished notification.
if notificationName == "block connected" &&
waddrmgr.IsError(err, waddrmgr.ErrBlockNotFound) &&
!w.ChainSynced() {
log.Debugf("Received block connected "+
"notification for height %v "+
"while rescanning",
n.(chain.BlockConnected).Height)
continue
}
log.Errorf("Unable to process chain backend "+
"%v notification: %v", notificationName,
err)
}
case <-w.quit:
return
}
}
}
// connectBlock handles a chain server notification by marking a wallet
// that's currently in-sync with the chain server as being synced up to
// the passed block.
func (w *Wallet) connectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
bs := waddrmgr.BlockStamp{
Height: b.Height,
Hash: b.Hash,
Timestamp: b.Time,
}
err := w.Manager.SetSyncedTo(addrmgrNs, &bs)
if err != nil {
return err
}
// Notify interested clients of the connected block.
//
// TODO: move all notifications outside of the database transaction.
w.NtfnServer.notifyAttachedBlock(dbtx, &b)
return nil
}
// disconnectBlock handles a chain server reorganize by rolling back all
// block history from the reorged block for a wallet in-sync with the chain
// server.
func (w *Wallet) disconnectBlock(dbtx walletdb.ReadWriteTx, b wtxmgr.BlockMeta) error {
addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)
if !w.ChainSynced() {
return nil
}
// Disconnect the removed block and all blocks after it if we know about
// the disconnected block. Otherwise, the block is in the future.
if b.Height <= w.Manager.SyncedTo().Height {
hash, err := w.Manager.BlockHash(addrmgrNs, b.Height)
if err != nil {
return err
}
if bytes.Equal(hash[:], b.Hash[:]) {
bs := waddrmgr.BlockStamp{
Height: b.Height - 1,
}
hash, err = w.Manager.BlockHash(addrmgrNs, bs.Height)
if err != nil {
return err
}
b.Hash = *hash
client := w.ChainClient()
header, err := client.GetBlockHeader(hash)
if err != nil {
return err
}
bs.Timestamp = header.Timestamp
err = w.Manager.SetSyncedTo(addrmgrNs, &bs)
if err != nil {
return err
}
err = w.TxStore.Rollback(txmgrNs, b.Height)
if err != nil {
return err
}
}
}
// Notify interested clients of the disconnected block.
w.NtfnServer.notifyDetachedBlock(&b.Hash)
return nil
}
func (w *Wallet) addRelevantTx(dbtx walletdb.ReadWriteTx, rec *wtxmgr.TxRecord, block *wtxmgr.BlockMeta) error {
addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
txmgrNs := dbtx.ReadWriteBucket(wtxmgrNamespaceKey)
// At the moment all notified transactions are assumed to actually be
// relevant. This assumption will not hold true when SPV support is
// added, but until then, simply insert the transaction because there
// should either be one or more relevant inputs or outputs.
err := w.TxStore.InsertTx(txmgrNs, rec, block)
if err != nil {
return err
}
// Check every output to determine whether it is controlled by a wallet
// key. If so, mark the output as a credit.
for i, output := range rec.MsgTx.TxOut {
_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript,
w.chainParams)
if err != nil {
// Non-standard outputs are skipped.
continue
}
for _, addr := range addrs {
ma, err := w.Manager.Address(addrmgrNs, addr)
if err == nil {
// TODO: Credits should be added with the
// account they belong to, so wtxmgr is able to
// track per-account balances.
err = w.TxStore.AddCredit(txmgrNs, rec, block, uint32(i),
ma.Internal())
if err != nil {
return err
}
err = w.Manager.MarkUsed(addrmgrNs, addr)
if err != nil {
return err
}
log.Debugf("Marked address %v used", addr)
continue
}
// Missing addresses are skipped. Other errors should
// be propagated.
if !waddrmgr.IsError(err, waddrmgr.ErrAddressNotFound) {
return err
}
}
}
// Send notification of mined or unmined transaction to any interested
// clients.
//
// TODO: Avoid the extra db hits.
if block == nil {
details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, nil)
if err != nil {
log.Errorf("Cannot query transaction details for notification: %v", err)
}
// It's possible that the transaction was not found within the
// wallet's set of unconfirmed transactions due to it already
// being confirmed, so we'll avoid notifying it.
//
// TODO(wilmer): ideally we should find the culprit to why we're
// receiving an additional unconfirmed chain.RelevantTx
// notification from the chain backend.
if details != nil {
w.NtfnServer.notifyUnminedTransaction(dbtx, details)
}
} else {
details, err := w.TxStore.UniqueTxDetails(txmgrNs, &rec.Hash, &block.Block)
if err != nil {
log.Errorf("Cannot query transaction details for notification: %v", err)
}
// We'll only notify the transaction if it was found within the
// wallet's set of confirmed transactions.
if details != nil {
w.NtfnServer.notifyMinedTransaction(dbtx, details, block)
}
}
return nil
}
// chainConn is an interface that abstracts the chain connection logic required
// to perform a wallet's birthday block sanity check.
type chainConn interface {
// GetBestBlock returns the hash and height of the best block known to
// the backend.
GetBestBlock() (*chainhash.Hash, int32, error)
// GetBlockHash returns the hash of the block with the given height.
GetBlockHash(int64) (*chainhash.Hash, error)
// GetBlockHeader returns the header for the block with the given hash.
GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader, error)
}
// birthdayStore is an interface that abstracts the wallet's sync-related
// information required to perform a birthday block sanity check.
type birthdayStore interface {
// Birthday returns the birthday timestamp of the wallet.
Birthday() time.Time
// BirthdayBlock returns the birthday block of the wallet. The boolean
// returned should signal whether the wallet has already verified the
// correctness of its birthday block.
BirthdayBlock() (waddrmgr.BlockStamp, bool, error)
// SetBirthdayBlock updates the birthday block of the wallet to the
// given block. The boolean can be used to signal whether this block
// should be sanity checked the next time the wallet starts.
//
// NOTE: This should also set the wallet's synced tip to reflect the new
// birthday block. This will allow the wallet to rescan from this point
// to detect any potentially missed events.
SetBirthdayBlock(waddrmgr.BlockStamp) error
}
// walletBirthdayStore is a wrapper around the wallet's database and address
// manager that satisfies the birthdayStore interface.
type walletBirthdayStore struct {
db walletdb.DB
manager *waddrmgr.Manager
}
var _ birthdayStore = (*walletBirthdayStore)(nil)
// Birthday returns the birthday timestamp of the wallet.
func (s *walletBirthdayStore) Birthday() time.Time {
return s.manager.Birthday()
}
// BirthdayBlock returns the birthday block of the wallet.
func (s *walletBirthdayStore) BirthdayBlock() (waddrmgr.BlockStamp, bool, error) {
var (
birthdayBlock waddrmgr.BlockStamp
birthdayBlockVerified bool
)
err := walletdb.View(s.db, func(tx walletdb.ReadTx) error {
var err error
ns := tx.ReadBucket(waddrmgrNamespaceKey)
birthdayBlock, birthdayBlockVerified, err = s.manager.BirthdayBlock(ns)
return err
})
return birthdayBlock, birthdayBlockVerified, err
}
// SetBirthdayBlock updates the birthday block of the wallet to the
// given block. The boolean can be used to signal whether this block
// should be sanity checked the next time the wallet starts.
//
// NOTE: This should also set the wallet's synced tip to reflect the new
// birthday block. This will allow the wallet to rescan from this point
// to detect any potentially missed events.
func (s *walletBirthdayStore) SetBirthdayBlock(block waddrmgr.BlockStamp) error {
return walletdb.Update(s.db, func(tx walletdb.ReadWriteTx) error {
ns := tx.ReadWriteBucket(waddrmgrNamespaceKey)
err := s.manager.SetBirthdayBlock(ns, block, true)
if err != nil {
return err
}
return s.manager.SetSyncedTo(ns, &block)
})
}
// birthdaySanityCheck is a helper function that ensures a birthday block
// correctly reflects the birthday timestamp within a reasonable timestamp
// delta. It's intended to be run after the wallet establishes its connection
// with the backend, but before it begins syncing. This is done as the second
// part to the wallet's address manager migration where we populate the birthday
// block to ensure we do not miss any relevant events throughout rescans.
// waddrmgr.ErrBirthdayBlockNotSet is returned if the birthday block has not
// been set yet.
func birthdaySanityCheck(chainConn chainConn,
birthdayStore birthdayStore) (*waddrmgr.BlockStamp, error) {
// We'll start by fetching our wallet's birthday timestamp and block.
birthdayTimestamp := birthdayStore.Birthday()
birthdayBlock, birthdayBlockVerified, err := birthdayStore.BirthdayBlock()
if err != nil {
return nil, err
}
// If the birthday block has already been verified to be correct, we can
// exit our sanity check to prevent potentially fetching a better
// candidate.
if birthdayBlockVerified {
log.Debugf("Birthday block has already been verified: "+
"height=%d, hash=%v", birthdayBlock.Height,
birthdayBlock.Hash)
return &birthdayBlock, nil
}
// Otherwise, we'll attempt to locate a better one now that we have
// access to the chain.
newBirthdayBlock, err := locateBirthdayBlock(chainConn, birthdayTimestamp)
if err != nil {
return nil, err
}
if err := birthdayStore.SetBirthdayBlock(*newBirthdayBlock); err != nil {
return nil, err
}
return newBirthdayBlock, nil
}

88
vendor/github.com/btcsuite/btcwallet/wallet/common.go generated vendored Normal file
View File

@ -0,0 +1,88 @@
// Copyright (c) 2016 The Decred developers
// 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 wallet
import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
// Note: The following common types should never reference the Wallet type.
// Long term goal is to move these to their own package so that the database
// access APIs can create them directly for the wallet to return.
// BlockIdentity identifies a block, or the lack of one (used to describe an
// unmined transaction).
type BlockIdentity struct {
Hash chainhash.Hash
Height int32
}
// None returns whether there is no block described by the instance. When
// associated with a transaction, this indicates the transaction is unmined.
func (b *BlockIdentity) None() bool {
// BUG: Because dcrwallet uses both 0 and -1 in various places to refer
// to an unmined transaction this must check against both and may not
// ever be usable to represent the genesis block.
return *b == BlockIdentity{Height: -1} || *b == BlockIdentity{}
}
// OutputKind describes a kind of transaction output. This is used to
// differentiate between coinbase, stakebase, and normal outputs.
type OutputKind byte
// Defined OutputKind constants
const (
OutputKindNormal OutputKind = iota
OutputKindCoinbase
)
// TransactionOutput describes an output that was or is at least partially
// controlled by the wallet. Depending on context, this could refer to an
// unspent output, or a spent one.
type TransactionOutput struct {
OutPoint wire.OutPoint
Output wire.TxOut
OutputKind OutputKind
// These should be added later when the DB can return them more
// efficiently:
//TxLockTime uint32
//TxExpiry uint32
ContainingBlock BlockIdentity
ReceiveTime time.Time
}
// OutputRedeemer identifies the transaction input which redeems an output.
type OutputRedeemer struct {
TxHash chainhash.Hash
InputIndex uint32
}
// P2SHMultiSigOutput describes a transaction output with a pay-to-script-hash
// output script and an imported redemption script. Along with common details
// of the output, this structure also includes the P2SH address the script was
// created from and the number of signatures required to redeem it.
//
// TODO: Could be useful to return how many of the required signatures can be
// created by this wallet.
type P2SHMultiSigOutput struct {
// TODO: Add a TransactionOutput member to this struct and remove these
// fields which are duplicated by it. This improves consistency. Only
// not done now because wtxmgr APIs don't support an efficient way of
// fetching other Transactionoutput data together with the rest of the
// multisig info.
OutPoint wire.OutPoint
OutputAmount btcutil.Amount
ContainingBlock BlockIdentity
P2SHAddress *btcutil.AddressScriptHash
RedeemScript []byte
M, N uint8 // M of N signatures required to redeem
Redeemer *OutputRedeemer // nil unless spent
}

290
vendor/github.com/btcsuite/btcwallet/wallet/createtx.go generated vendored Normal file
View File

@ -0,0 +1,290 @@
// Copyright (c) 2013-2017 The btcsuite developers
// 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 wallet
import (
"fmt"
"sort"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// byAmount defines the methods needed to satisify sort.Interface to
// sort credits by their output amount.
type byAmount []wtxmgr.Credit
func (s byAmount) Len() int { return len(s) }
func (s byAmount) Less(i, j int) bool { return s[i].Amount < s[j].Amount }
func (s byAmount) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func makeInputSource(eligible []wtxmgr.Credit) txauthor.InputSource {
// Pick largest outputs first. This is only done for compatibility with
// previous tx creation code, not because it's a good idea.
sort.Sort(sort.Reverse(byAmount(eligible)))
// Current inputs and their total value. These are closed over by the
// returned input source and reused across multiple calls.
currentTotal := btcutil.Amount(0)
currentInputs := make([]*wire.TxIn, 0, len(eligible))
currentScripts := make([][]byte, 0, len(eligible))
currentInputValues := make([]btcutil.Amount, 0, len(eligible))
return func(target btcutil.Amount) (btcutil.Amount, []*wire.TxIn,
[]btcutil.Amount, [][]byte, error) {
for currentTotal < target && len(eligible) != 0 {
nextCredit := &eligible[0]
eligible = eligible[1:]
nextInput := wire.NewTxIn(&nextCredit.OutPoint, nil, nil)
currentTotal += nextCredit.Amount
currentInputs = append(currentInputs, nextInput)
currentScripts = append(currentScripts, nextCredit.PkScript)
currentInputValues = append(currentInputValues, nextCredit.Amount)
}
return currentTotal, currentInputs, currentInputValues, currentScripts, nil
}
}
// secretSource is an implementation of txauthor.SecretSource for the wallet's
// address manager.
type secretSource struct {
*waddrmgr.Manager
addrmgrNs walletdb.ReadBucket
}
func (s secretSource) GetKey(addr btcutil.Address) (*btcec.PrivateKey, bool, error) {
ma, err := s.Address(s.addrmgrNs, addr)
if err != nil {
return nil, false, err
}
mpka, ok := ma.(waddrmgr.ManagedPubKeyAddress)
if !ok {
e := fmt.Errorf("managed address type for %v is `%T` but "+
"want waddrmgr.ManagedPubKeyAddress", addr, ma)
return nil, false, e
}
privKey, err := mpka.PrivKey()
if err != nil {
return nil, false, err
}
return privKey, ma.Compressed(), nil
}
func (s secretSource) GetScript(addr btcutil.Address) ([]byte, error) {
ma, err := s.Address(s.addrmgrNs, addr)
if err != nil {
return nil, err
}
msa, ok := ma.(waddrmgr.ManagedScriptAddress)
if !ok {
e := fmt.Errorf("managed address type for %v is `%T` but "+
"want waddrmgr.ManagedScriptAddress", addr, ma)
return nil, e
}
return msa.Script()
}
// txToOutputs creates a signed transaction which includes each output from
// outputs. Previous outputs to reedeem are chosen from the passed account's
// UTXO set and minconf policy. An additional output may be added to return
// change to the wallet. An appropriate fee is included based on the wallet's
// current relay fee. The wallet must be unlocked to create the transaction.
//
// NOTE: The dryRun argument can be set true to create a tx that doesn't alter
// the database. A tx created with this set to true will intentionally have no
// input scripts added and SHOULD NOT be broadcasted.
func (w *Wallet) txToOutputs(outputs []*wire.TxOut, account uint32,
minconf int32, feeSatPerKb btcutil.Amount, dryRun bool) (
tx *txauthor.AuthoredTx, err error) {
chainClient, err := w.requireChainClient()
if err != nil {
return nil, err
}
dbtx, err := w.db.BeginReadWriteTx()
if err != nil {
return nil, err
}
defer dbtx.Rollback()
addrmgrNs := dbtx.ReadWriteBucket(waddrmgrNamespaceKey)
// Get current block's height and hash.
bs, err := chainClient.BlockStamp()
if err != nil {
return nil, err
}
eligible, err := w.findEligibleOutputs(dbtx, account, minconf, bs)
if err != nil {
return nil, err
}
inputSource := makeInputSource(eligible)
changeSource := func() ([]byte, error) {
// Derive the change output script. We'll use the default key
// scope responsible for P2WPKH addresses to do so. As a hack to
// allow spending from the imported account, change addresses
// are created from account 0.
var changeAddr btcutil.Address
var err error
changeKeyScope := waddrmgr.KeyScopeBIP0084
if account == waddrmgr.ImportedAddrAccount {
changeAddr, err = w.newChangeAddress(
addrmgrNs, 0, changeKeyScope,
)
} else {
changeAddr, err = w.newChangeAddress(
addrmgrNs, account, changeKeyScope,
)
}
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(changeAddr)
}
tx, err = txauthor.NewUnsignedTransaction(outputs, feeSatPerKb,
inputSource, changeSource)
if err != nil {
return nil, err
}
// Randomize change position, if change exists, before signing. This
// doesn't affect the serialize size, so the change amount will still
// be valid.
if tx.ChangeIndex >= 0 {
tx.RandomizeChangePosition()
}
// If a dry run was requested, we return now before adding the input
// scripts, and don't commit the database transaction. The DB will be
// rolled back when this method returns to ensure the dry run didn't
// alter the DB in any way.
if dryRun {
return tx, nil
}
err = tx.AddAllInputScripts(secretSource{w.Manager, addrmgrNs})
if err != nil {
return nil, err
}
err = validateMsgTx(tx.Tx, tx.PrevScripts, tx.PrevInputValues)
if err != nil {
return nil, err
}
if err := dbtx.Commit(); err != nil {
return nil, err
}
if tx.ChangeIndex >= 0 && account == waddrmgr.ImportedAddrAccount {
changeAmount := btcutil.Amount(tx.Tx.TxOut[tx.ChangeIndex].Value)
log.Warnf("Spend from imported account produced change: moving"+
" %v from imported account into default account.", changeAmount)
}
// Finally, we'll request the backend to notify us of the transaction
// that pays to the change address, if there is one, when it confirms.
if tx.ChangeIndex >= 0 {
changePkScript := tx.Tx.TxOut[tx.ChangeIndex].PkScript
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
changePkScript, w.chainParams,
)
if err != nil {
return nil, err
}
if err := chainClient.NotifyReceived(addrs); err != nil {
return nil, err
}
}
return tx, nil
}
func (w *Wallet) findEligibleOutputs(dbtx walletdb.ReadTx, account uint32, minconf int32, bs *waddrmgr.BlockStamp) ([]wtxmgr.Credit, error) {
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
unspent, err := w.TxStore.UnspentOutputs(txmgrNs)
if err != nil {
return nil, err
}
// TODO: Eventually all of these filters (except perhaps output locking)
// should be handled by the call to UnspentOutputs (or similar).
// Because one of these filters requires matching the output script to
// the desired account, this change depends on making wtxmgr a waddrmgr
// dependancy and requesting unspent outputs for a single account.
eligible := make([]wtxmgr.Credit, 0, len(unspent))
for i := range unspent {
output := &unspent[i]
// Only include this output if it meets the required number of
// confirmations. Coinbase transactions must have have reached
// maturity before their outputs may be spent.
if !confirmed(minconf, output.Height, bs.Height) {
continue
}
if output.FromCoinBase {
target := int32(w.chainParams.CoinbaseMaturity)
if !confirmed(target, output.Height, bs.Height) {
continue
}
}
// Locked unspent outputs are skipped.
if w.LockedOutpoint(output.OutPoint) {
continue
}
// Only include the output if it is associated with the passed
// account.
//
// TODO: Handle multisig outputs by determining if enough of the
// addresses are controlled.
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
output.PkScript, w.chainParams)
if err != nil || len(addrs) != 1 {
continue
}
_, addrAcct, err := w.Manager.AddrAccount(addrmgrNs, addrs[0])
if err != nil || addrAcct != account {
continue
}
eligible = append(eligible, *output)
}
return eligible, nil
}
// validateMsgTx verifies transaction input scripts for tx. All previous output
// scripts from outputs redeemed by the transaction, in the same order they are
// spent, must be passed in the prevScripts slice.
func validateMsgTx(tx *wire.MsgTx, prevScripts [][]byte, inputValues []btcutil.Amount) error {
hashCache := txscript.NewTxSigHashes(tx)
for i, prevScript := range prevScripts {
vm, err := txscript.NewEngine(prevScript, tx, i,
txscript.StandardVerifyFlags, nil, hashCache, int64(inputValues[i]))
if err != nil {
return fmt.Errorf("cannot create script engine: %s", err)
}
err = vm.Execute()
if err != nil {
return fmt.Errorf("cannot validate transaction: %s", err)
}
}
return nil
}

View File

@ -0,0 +1,31 @@
// 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 wallet
import (
"fmt"
"os"
)
// checkCreateDir checks that the path exists and is a directory.
// If path does not exist, it is created.
func checkCreateDir(path string) error {
if fi, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
// Attempt data directory creation
if err = os.MkdirAll(path, 0700); err != nil {
return fmt.Errorf("cannot create directory: %s", err)
}
} else {
return fmt.Errorf("error checking directory: %s", err)
}
} else {
if !fi.IsDir() {
return fmt.Errorf("path '%s' is not a directory", path)
}
}
return nil
}

12
vendor/github.com/btcsuite/btcwallet/wallet/doc.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// 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 wallet provides ...
TODO: Flesh out this section
Overview
*/
package wallet

284
vendor/github.com/btcsuite/btcwallet/wallet/loader.go generated vendored Normal file
View File

@ -0,0 +1,284 @@
// 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 wallet
import (
"errors"
"os"
"path/filepath"
"sync"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcwallet/internal/prompt"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
)
const (
walletDbName = "wallet.db"
)
var (
// ErrLoaded describes the error condition of attempting to load or
// create a wallet when the loader has already done so.
ErrLoaded = errors.New("wallet already loaded")
// ErrNotLoaded describes the error condition of attempting to close a
// loaded wallet when a wallet has not been loaded.
ErrNotLoaded = errors.New("wallet is not loaded")
// ErrExists describes the error condition of attempting to create a new
// wallet when one exists already.
ErrExists = errors.New("wallet already exists")
)
// Loader implements the creating of new and opening of existing wallets, while
// providing a callback system for other subsystems to handle the loading of a
// wallet. This is primarily intended for use by the RPC servers, to enable
// methods and services which require the wallet when the wallet is loaded by
// another subsystem.
//
// Loader is safe for concurrent access.
type Loader struct {
callbacks []func(*Wallet)
chainParams *chaincfg.Params
dbDirPath string
noFreelistSync bool
recoveryWindow uint32
wallet *Wallet
db walletdb.DB
mu sync.Mutex
}
// NewLoader constructs a Loader with an optional recovery window. If the
// recovery window is non-zero, the wallet will attempt to recovery addresses
// starting from the last SyncedTo height.
func NewLoader(chainParams *chaincfg.Params, dbDirPath string,
noFreelistSync bool, recoveryWindow uint32) *Loader {
return &Loader{
chainParams: chainParams,
dbDirPath: dbDirPath,
noFreelistSync: noFreelistSync,
recoveryWindow: recoveryWindow,
}
}
// onLoaded executes each added callback and prevents loader from loading any
// additional wallets. Requires mutex to be locked.
func (l *Loader) onLoaded(w *Wallet, db walletdb.DB) {
for _, fn := range l.callbacks {
fn(w)
}
l.wallet = w
l.db = db
l.callbacks = nil // not needed anymore
}
// RunAfterLoad adds a function to be executed when the loader creates or opens
// a wallet. Functions are executed in a single goroutine in the order they are
// added.
func (l *Loader) RunAfterLoad(fn func(*Wallet)) {
l.mu.Lock()
if l.wallet != nil {
w := l.wallet
l.mu.Unlock()
fn(w)
} else {
l.callbacks = append(l.callbacks, fn)
l.mu.Unlock()
}
}
// CreateNewWallet creates a new wallet using the provided public and private
// passphrases. The seed is optional. If non-nil, addresses are derived from
// this seed. If nil, a secure random seed is generated.
func (l *Loader) CreateNewWallet(pubPassphrase, privPassphrase, seed []byte,
bday time.Time) (*Wallet, error) {
return l.createNewWallet(
pubPassphrase, privPassphrase, seed, bday, false,
)
}
// CreateNewWatchingOnlyWallet creates a new wallet using the provided
// public passphrase. No seed or private passphrase may be provided
// since the wallet is watching-only.
func (l *Loader) CreateNewWatchingOnlyWallet(pubPassphrase []byte,
bday time.Time) (*Wallet, error) {
return l.createNewWallet(
pubPassphrase, nil, nil, bday, true,
)
}
func (l *Loader) createNewWallet(pubPassphrase, privPassphrase,
seed []byte, bday time.Time, isWatchingOnly bool) (*Wallet, error) {
defer l.mu.Unlock()
l.mu.Lock()
if l.wallet != nil {
return nil, ErrLoaded
}
dbPath := filepath.Join(l.dbDirPath, walletDbName)
exists, err := fileExists(dbPath)
if err != nil {
return nil, err
}
if exists {
return nil, ErrExists
}
// Create the wallet database backed by bolt db.
err = os.MkdirAll(l.dbDirPath, 0700)
if err != nil {
return nil, err
}
db, err := walletdb.Create("bdb", dbPath, l.noFreelistSync)
if err != nil {
return nil, err
}
// Initialize the newly created database for the wallet before opening.
if isWatchingOnly {
err = CreateWatchingOnly(db, pubPassphrase, l.chainParams, bday)
if err != nil {
return nil, err
}
} else {
err = Create(
db, pubPassphrase, privPassphrase, seed, l.chainParams, bday,
)
if err != nil {
return nil, err
}
}
// Open the newly-created wallet.
w, err := Open(db, pubPassphrase, nil, l.chainParams, l.recoveryWindow)
if err != nil {
return nil, err
}
w.Start()
l.onLoaded(w, db)
return w, nil
}
var errNoConsole = errors.New("db upgrade requires console access for additional input")
func noConsole() ([]byte, error) {
return nil, errNoConsole
}
// OpenExistingWallet opens the wallet from the loader's wallet database path
// and the public passphrase. If the loader is being called by a context where
// standard input prompts may be used during wallet upgrades, setting
// canConsolePrompt will enables these prompts.
func (l *Loader) OpenExistingWallet(pubPassphrase []byte, canConsolePrompt bool) (*Wallet, error) {
defer l.mu.Unlock()
l.mu.Lock()
if l.wallet != nil {
return nil, ErrLoaded
}
// Ensure that the network directory exists.
if err := checkCreateDir(l.dbDirPath); err != nil {
return nil, err
}
// Open the database using the boltdb backend.
dbPath := filepath.Join(l.dbDirPath, walletDbName)
db, err := walletdb.Open("bdb", dbPath, l.noFreelistSync)
if err != nil {
log.Errorf("Failed to open database: %v", err)
return nil, err
}
var cbs *waddrmgr.OpenCallbacks
if canConsolePrompt {
cbs = &waddrmgr.OpenCallbacks{
ObtainSeed: prompt.ProvideSeed,
ObtainPrivatePass: prompt.ProvidePrivPassphrase,
}
} else {
cbs = &waddrmgr.OpenCallbacks{
ObtainSeed: noConsole,
ObtainPrivatePass: noConsole,
}
}
w, err := Open(db, pubPassphrase, cbs, l.chainParams, l.recoveryWindow)
if err != nil {
// If opening the wallet fails (e.g. because of wrong
// passphrase), we must close the backing database to
// allow future calls to walletdb.Open().
e := db.Close()
if e != nil {
log.Warnf("Error closing database: %v", e)
}
return nil, err
}
w.Start()
l.onLoaded(w, db)
return w, nil
}
// WalletExists returns whether a file exists at the loader's database path.
// This may return an error for unexpected I/O failures.
func (l *Loader) WalletExists() (bool, error) {
dbPath := filepath.Join(l.dbDirPath, walletDbName)
return fileExists(dbPath)
}
// LoadedWallet returns the loaded wallet, if any, and a bool for whether the
// wallet has been loaded or not. If true, the wallet pointer should be safe to
// dereference.
func (l *Loader) LoadedWallet() (*Wallet, bool) {
l.mu.Lock()
w := l.wallet
l.mu.Unlock()
return w, w != nil
}
// UnloadWallet stops the loaded wallet, if any, and closes the wallet database.
// This returns ErrNotLoaded if the wallet has not been loaded with
// CreateNewWallet or LoadExistingWallet. The Loader may be reused if this
// function returns without error.
func (l *Loader) UnloadWallet() error {
defer l.mu.Unlock()
l.mu.Lock()
if l.wallet == nil {
return ErrNotLoaded
}
l.wallet.Stop()
l.wallet.WaitForShutdown()
err := l.db.Close()
if err != nil {
return err
}
l.wallet = nil
l.db = nil
return nil
}
func fileExists(filePath string) (bool, error) {
_, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
return true, nil
}

65
vendor/github.com/btcsuite/btcwallet/wallet/log.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
// 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 wallet
import (
"github.com/btcsuite/btclog"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb/migration"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// log is a logger that is initialized with no output filters. This
// means the package will not perform any logging by default until the caller
// requests it.
var log btclog.Logger
// The default amount of logging is none.
func init() {
DisableLog()
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
UseLogger(btclog.Disabled)
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger
migration.UseLogger(logger)
waddrmgr.UseLogger(logger)
wtxmgr.UseLogger(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)
}
// pickNoun returns the singular or plural form of a noun depending
// on the count n.
func pickNoun(n int, singular, plural string) string {
if n == 1 {
return singular
}
return plural
}

85
vendor/github.com/btcsuite/btcwallet/wallet/mock.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
package wallet
import (
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/waddrmgr"
)
type mockChainClient struct {
}
var _ chain.Interface = (*mockChainClient)(nil)
func (m *mockChainClient) Start() error {
return nil
}
func (m *mockChainClient) Stop() {
}
func (m *mockChainClient) WaitForShutdown() {}
func (m *mockChainClient) GetBestBlock() (*chainhash.Hash, int32, error) {
return nil, 0, nil
}
func (m *mockChainClient) GetBlock(*chainhash.Hash) (*wire.MsgBlock, error) {
return nil, nil
}
func (m *mockChainClient) GetBlockHash(int64) (*chainhash.Hash, error) {
return nil, nil
}
func (m *mockChainClient) GetBlockHeader(*chainhash.Hash) (*wire.BlockHeader,
error) {
return nil, nil
}
func (m *mockChainClient) IsCurrent() bool {
return false
}
func (m *mockChainClient) FilterBlocks(*chain.FilterBlocksRequest) (
*chain.FilterBlocksResponse, error) {
return nil, nil
}
func (m *mockChainClient) BlockStamp() (*waddrmgr.BlockStamp, error) {
return &waddrmgr.BlockStamp{
Height: 500000,
Hash: chainhash.Hash{},
Timestamp: time.Unix(1234, 0),
}, nil
}
func (m *mockChainClient) SendRawTransaction(*wire.MsgTx, bool) (
*chainhash.Hash, error) {
return nil, nil
}
func (m *mockChainClient) Rescan(*chainhash.Hash, []btcutil.Address,
map[wire.OutPoint]btcutil.Address) error {
return nil
}
func (m *mockChainClient) NotifyReceived([]btcutil.Address) error {
return nil
}
func (m *mockChainClient) NotifyBlocks() error {
return nil
}
func (m *mockChainClient) Notifications() <-chan interface{} {
return nil
}
func (m *mockChainClient) BackEnd() string {
return "mock"
}

114
vendor/github.com/btcsuite/btcwallet/wallet/multisig.go generated vendored Normal file
View File

@ -0,0 +1,114 @@
// Copyright (c) 2017 The btcsuite developers
// Copyright (c) 2016 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wallet
import (
"errors"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
)
// MakeMultiSigScript creates a multi-signature script that can be redeemed with
// nRequired signatures of the passed keys and addresses. If the address is a
// P2PKH address, the associated pubkey is looked up by the wallet if possible,
// otherwise an error is returned for a missing pubkey.
//
// This function only works with pubkeys and P2PKH addresses derived from them.
func (w *Wallet) MakeMultiSigScript(addrs []btcutil.Address, nRequired int) ([]byte, error) {
pubKeys := make([]*btcutil.AddressPubKey, len(addrs))
var dbtx walletdb.ReadTx
var addrmgrNs walletdb.ReadBucket
defer func() {
if dbtx != nil {
dbtx.Rollback()
}
}()
// The address list will made up either of addreseses (pubkey hash), for
// which we need to look up the keys in wallet, straight pubkeys, or a
// mixture of the two.
for i, addr := range addrs {
switch addr := addr.(type) {
default:
return nil, errors.New("cannot make multisig script for " +
"a non-secp256k1 public key or P2PKH address")
case *btcutil.AddressPubKey:
pubKeys[i] = addr
case *btcutil.AddressPubKeyHash:
if dbtx == nil {
var err error
dbtx, err = w.db.BeginReadTx()
if err != nil {
return nil, err
}
addrmgrNs = dbtx.ReadBucket(waddrmgrNamespaceKey)
}
addrInfo, err := w.Manager.Address(addrmgrNs, addr)
if err != nil {
return nil, err
}
serializedPubKey := addrInfo.(waddrmgr.ManagedPubKeyAddress).
PubKey().SerializeCompressed()
pubKeyAddr, err := btcutil.NewAddressPubKey(
serializedPubKey, w.chainParams)
if err != nil {
return nil, err
}
pubKeys[i] = pubKeyAddr
}
}
return txscript.MultiSigScript(pubKeys, nRequired)
}
// ImportP2SHRedeemScript adds a P2SH redeem script to the wallet.
func (w *Wallet) ImportP2SHRedeemScript(script []byte) (*btcutil.AddressScriptHash, error) {
var p2shAddr *btcutil.AddressScriptHash
err := walletdb.Update(w.db, func(tx walletdb.ReadWriteTx) error {
addrmgrNs := tx.ReadWriteBucket(waddrmgrNamespaceKey)
// TODO(oga) blockstamp current block?
bs := &waddrmgr.BlockStamp{
Hash: *w.ChainParams().GenesisHash,
Height: 0,
}
// As this is a regular P2SH script, we'll import this into the
// BIP0044 scope.
bip44Mgr, err := w.Manager.FetchScopedKeyManager(
waddrmgr.KeyScopeBIP0084,
)
if err != nil {
return err
}
addrInfo, err := bip44Mgr.ImportScript(addrmgrNs, script, bs)
if err != nil {
// Don't care if it's already there, but still have to
// set the p2shAddr since the address manager didn't
// return anything useful.
if waddrmgr.IsError(err, waddrmgr.ErrDuplicateAddress) {
// This function will never error as it always
// hashes the script to the correct length.
p2shAddr, _ = btcutil.NewAddressScriptHash(script,
w.chainParams)
return nil
}
return err
}
p2shAddr = addrInfo.Address().(*btcutil.AddressScriptHash)
return nil
})
return p2shAddr, err
}

View File

@ -0,0 +1,637 @@
// 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 wallet
import (
"bytes"
"sync"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// TODO: It would be good to send errors during notification creation to the rpc
// server instead of just logging them here so the client is aware that wallet
// isn't working correctly and notifications are missing.
// TODO: Anything dealing with accounts here is expensive because the database
// is not organized correctly for true account support, but do the slow thing
// instead of the easy thing since the db can be fixed later, and we want the
// api correct now.
// NotificationServer is a server that interested clients may hook into to
// receive notifications of changes in a wallet. A client is created for each
// registered notification. Clients are guaranteed to receive messages in the
// order wallet created them, but there is no guaranteed synchronization between
// different clients.
type NotificationServer struct {
transactions []chan *TransactionNotifications
currentTxNtfn *TransactionNotifications // coalesce this since wallet does not add mined txs together
spentness map[uint32][]chan *SpentnessNotifications
accountClients []chan *AccountNotification
mu sync.Mutex // Only protects registered client channels
wallet *Wallet // smells like hacks
}
func newNotificationServer(wallet *Wallet) *NotificationServer {
return &NotificationServer{
spentness: make(map[uint32][]chan *SpentnessNotifications),
wallet: wallet,
}
}
func lookupInputAccount(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails, deb wtxmgr.DebitRecord) uint32 {
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
// TODO: Debits should record which account(s?) they
// debit from so this doesn't need to be looked up.
prevOP := &details.MsgTx.TxIn[deb.Index].PreviousOutPoint
prev, err := w.TxStore.TxDetails(txmgrNs, &prevOP.Hash)
if err != nil {
log.Errorf("Cannot query previous transaction details for %v: %v", prevOP.Hash, err)
return 0
}
if prev == nil {
log.Errorf("Missing previous transaction %v", prevOP.Hash)
return 0
}
prevOut := prev.MsgTx.TxOut[prevOP.Index]
_, addrs, _, err := txscript.ExtractPkScriptAddrs(prevOut.PkScript, w.chainParams)
var inputAcct uint32
if err == nil && len(addrs) > 0 {
_, inputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
}
if err != nil {
log.Errorf("Cannot fetch account for previous output %v: %v", prevOP, err)
inputAcct = 0
}
return inputAcct
}
func lookupOutputChain(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails,
cred wtxmgr.CreditRecord) (account uint32, internal bool) {
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
output := details.MsgTx.TxOut[cred.Index]
_, addrs, _, err := txscript.ExtractPkScriptAddrs(output.PkScript, w.chainParams)
var ma waddrmgr.ManagedAddress
if err == nil && len(addrs) > 0 {
ma, err = w.Manager.Address(addrmgrNs, addrs[0])
}
if err != nil {
log.Errorf("Cannot fetch account for wallet output: %v", err)
} else {
account = ma.Account()
internal = ma.Internal()
}
return
}
func makeTxSummary(dbtx walletdb.ReadTx, w *Wallet, details *wtxmgr.TxDetails) TransactionSummary {
serializedTx := details.SerializedTx
if serializedTx == nil {
var buf bytes.Buffer
err := details.MsgTx.Serialize(&buf)
if err != nil {
log.Errorf("Transaction serialization: %v", err)
}
serializedTx = buf.Bytes()
}
var fee btcutil.Amount
if len(details.Debits) == len(details.MsgTx.TxIn) {
for _, deb := range details.Debits {
fee += deb.Amount
}
for _, txOut := range details.MsgTx.TxOut {
fee -= btcutil.Amount(txOut.Value)
}
}
var inputs []TransactionSummaryInput
if len(details.Debits) != 0 {
inputs = make([]TransactionSummaryInput, len(details.Debits))
for i, d := range details.Debits {
inputs[i] = TransactionSummaryInput{
Index: d.Index,
PreviousAccount: lookupInputAccount(dbtx, w, details, d),
PreviousAmount: d.Amount,
}
}
}
outputs := make([]TransactionSummaryOutput, 0, len(details.MsgTx.TxOut))
for i := range details.MsgTx.TxOut {
credIndex := len(outputs)
mine := len(details.Credits) > credIndex && details.Credits[credIndex].Index == uint32(i)
if !mine {
continue
}
acct, internal := lookupOutputChain(dbtx, w, details, details.Credits[credIndex])
output := TransactionSummaryOutput{
Index: uint32(i),
Account: acct,
Internal: internal,
}
outputs = append(outputs, output)
}
return TransactionSummary{
Hash: &details.Hash,
Transaction: serializedTx,
MyInputs: inputs,
MyOutputs: outputs,
Fee: fee,
Timestamp: details.Received.Unix(),
Label: details.Label,
}
}
func totalBalances(dbtx walletdb.ReadTx, w *Wallet, m map[uint32]btcutil.Amount) error {
addrmgrNs := dbtx.ReadBucket(waddrmgrNamespaceKey)
unspent, err := w.TxStore.UnspentOutputs(dbtx.ReadBucket(wtxmgrNamespaceKey))
if err != nil {
return err
}
for i := range unspent {
output := &unspent[i]
var outputAcct uint32
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
output.PkScript, w.chainParams)
if err == nil && len(addrs) > 0 {
_, outputAcct, err = w.Manager.AddrAccount(addrmgrNs, addrs[0])
}
if err == nil {
_, ok := m[outputAcct]
if ok {
m[outputAcct] += output.Amount
}
}
}
return nil
}
func flattenBalanceMap(m map[uint32]btcutil.Amount) []AccountBalance {
s := make([]AccountBalance, 0, len(m))
for k, v := range m {
s = append(s, AccountBalance{Account: k, TotalBalance: v})
}
return s
}
func relevantAccounts(w *Wallet, m map[uint32]btcutil.Amount, txs []TransactionSummary) {
for _, tx := range txs {
for _, d := range tx.MyInputs {
m[d.PreviousAccount] = 0
}
for _, c := range tx.MyOutputs {
m[c.Account] = 0
}
}
}
func (s *NotificationServer) notifyUnminedTransaction(dbtx walletdb.ReadTx, details *wtxmgr.TxDetails) {
// Sanity check: should not be currently coalescing a notification for
// mined transactions at the same time that an unmined tx is notified.
if s.currentTxNtfn != nil {
log.Errorf("Notifying unmined tx notification (%s) while creating notification for blocks",
details.Hash)
}
defer s.mu.Unlock()
s.mu.Lock()
clients := s.transactions
if len(clients) == 0 {
return
}
unminedTxs := []TransactionSummary{makeTxSummary(dbtx, s.wallet, details)}
unminedHashes, err := s.wallet.TxStore.UnminedTxHashes(dbtx.ReadBucket(wtxmgrNamespaceKey))
if err != nil {
log.Errorf("Cannot fetch unmined transaction hashes: %v", err)
return
}
bals := make(map[uint32]btcutil.Amount)
relevantAccounts(s.wallet, bals, unminedTxs)
err = totalBalances(dbtx, s.wallet, bals)
if err != nil {
log.Errorf("Cannot determine balances for relevant accounts: %v", err)
return
}
n := &TransactionNotifications{
UnminedTransactions: unminedTxs,
UnminedTransactionHashes: unminedHashes,
NewBalances: flattenBalanceMap(bals),
}
for _, c := range clients {
c <- n
}
}
func (s *NotificationServer) notifyDetachedBlock(hash *chainhash.Hash) {
if s.currentTxNtfn == nil {
s.currentTxNtfn = &TransactionNotifications{}
}
s.currentTxNtfn.DetachedBlocks = append(s.currentTxNtfn.DetachedBlocks, hash)
}
func (s *NotificationServer) notifyMinedTransaction(dbtx walletdb.ReadTx, details *wtxmgr.TxDetails, block *wtxmgr.BlockMeta) {
if s.currentTxNtfn == nil {
s.currentTxNtfn = &TransactionNotifications{}
}
n := len(s.currentTxNtfn.AttachedBlocks)
if n == 0 || *s.currentTxNtfn.AttachedBlocks[n-1].Hash != block.Hash {
s.currentTxNtfn.AttachedBlocks = append(s.currentTxNtfn.AttachedBlocks, Block{
Hash: &block.Hash,
Height: block.Height,
Timestamp: block.Time.Unix(),
})
n++
}
txs := s.currentTxNtfn.AttachedBlocks[n-1].Transactions
s.currentTxNtfn.AttachedBlocks[n-1].Transactions =
append(txs, makeTxSummary(dbtx, s.wallet, details))
}
func (s *NotificationServer) notifyAttachedBlock(dbtx walletdb.ReadTx, block *wtxmgr.BlockMeta) {
if s.currentTxNtfn == nil {
s.currentTxNtfn = &TransactionNotifications{}
}
// Add block details if it wasn't already included for previously
// notified mined transactions.
n := len(s.currentTxNtfn.AttachedBlocks)
if n == 0 || *s.currentTxNtfn.AttachedBlocks[n-1].Hash != block.Hash {
s.currentTxNtfn.AttachedBlocks = append(s.currentTxNtfn.AttachedBlocks, Block{
Hash: &block.Hash,
Height: block.Height,
Timestamp: block.Time.Unix(),
})
}
// For now (until notification coalescing isn't necessary) just use
// chain length to determine if this is the new best block.
if s.wallet.ChainSynced() {
if len(s.currentTxNtfn.DetachedBlocks) >= len(s.currentTxNtfn.AttachedBlocks) {
return
}
}
defer s.mu.Unlock()
s.mu.Lock()
clients := s.transactions
if len(clients) == 0 {
s.currentTxNtfn = nil
return
}
// The UnminedTransactions field is intentionally not set. Since the
// hashes of all detached blocks are reported, and all transactions
// moved from a mined block back to unconfirmed are either in the
// UnminedTransactionHashes slice or don't exist due to conflicting with
// a mined transaction in the new best chain, there is no possiblity of
// a new, previously unseen transaction appearing in unconfirmed.
txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
unminedHashes, err := s.wallet.TxStore.UnminedTxHashes(txmgrNs)
if err != nil {
log.Errorf("Cannot fetch unmined transaction hashes: %v", err)
return
}
s.currentTxNtfn.UnminedTransactionHashes = unminedHashes
bals := make(map[uint32]btcutil.Amount)
for _, b := range s.currentTxNtfn.AttachedBlocks {
relevantAccounts(s.wallet, bals, b.Transactions)
}
err = totalBalances(dbtx, s.wallet, bals)
if err != nil {
log.Errorf("Cannot determine balances for relevant accounts: %v", err)
return
}
s.currentTxNtfn.NewBalances = flattenBalanceMap(bals)
for _, c := range clients {
c <- s.currentTxNtfn
}
s.currentTxNtfn = nil
}
// TransactionNotifications is a notification of changes to the wallet's
// transaction set and the current chain tip that wallet is considered to be
// synced with. All transactions added to the blockchain are organized by the
// block they were mined in.
//
// During a chain switch, all removed block hashes are included. Detached
// blocks are sorted in the reverse order they were mined. Attached blocks are
// sorted in the order mined.
//
// All newly added unmined transactions are included. Removed unmined
// transactions are not explicitly included. Instead, the hashes of all
// transactions still unmined are included.
//
// If any transactions were involved, each affected account's new total balance
// is included.
//
// TODO: Because this includes stuff about blocks and can be fired without any
// changes to transactions, it needs a better name.
type TransactionNotifications struct {
AttachedBlocks []Block
DetachedBlocks []*chainhash.Hash
UnminedTransactions []TransactionSummary
UnminedTransactionHashes []*chainhash.Hash
NewBalances []AccountBalance
}
// Block contains the properties and all relevant transactions of an attached
// block.
type Block struct {
Hash *chainhash.Hash
Height int32
Timestamp int64
Transactions []TransactionSummary
}
// TransactionSummary contains a transaction relevant to the wallet and marks
// which inputs and outputs were relevant.
type TransactionSummary struct {
Hash *chainhash.Hash
Transaction []byte
MyInputs []TransactionSummaryInput
MyOutputs []TransactionSummaryOutput
Fee btcutil.Amount
Timestamp int64
Label string
}
// TransactionSummaryInput describes a transaction input that is relevant to the
// wallet. The Index field marks the transaction input index of the transaction
// (not included here). The PreviousAccount and PreviousAmount fields describe
// how much this input debits from a wallet account.
type TransactionSummaryInput struct {
Index uint32
PreviousAccount uint32
PreviousAmount btcutil.Amount
}
// TransactionSummaryOutput describes wallet properties of a transaction output
// controlled by the wallet. The Index field marks the transaction output index
// of the transaction (not included here).
type TransactionSummaryOutput struct {
Index uint32
Account uint32
Internal bool
}
// AccountBalance associates a total (zero confirmation) balance with an
// account. Balances for other minimum confirmation counts require more
// expensive logic and it is not clear which minimums a client is interested in,
// so they are not included.
type AccountBalance struct {
Account uint32
TotalBalance btcutil.Amount
}
// TransactionNotificationsClient receives TransactionNotifications from the
// NotificationServer over the channel C.
type TransactionNotificationsClient struct {
C <-chan *TransactionNotifications
server *NotificationServer
}
// TransactionNotifications returns a client for receiving
// TransactionNotifiations notifications over a channel. The channel is
// unbuffered.
//
// When finished, the Done method should be called on the client to disassociate
// it from the server.
func (s *NotificationServer) TransactionNotifications() TransactionNotificationsClient {
c := make(chan *TransactionNotifications)
s.mu.Lock()
s.transactions = append(s.transactions, c)
s.mu.Unlock()
return TransactionNotificationsClient{
C: c,
server: s,
}
}
// Done deregisters the client from the server and drains any remaining
// messages. It must be called exactly once when the client is finished
// receiving notifications.
func (c *TransactionNotificationsClient) Done() {
go func() {
// Drain notifications until the client channel is removed from
// the server and closed.
for range c.C {
}
}()
go func() {
s := c.server
s.mu.Lock()
clients := s.transactions
for i, ch := range clients {
if c.C == ch {
clients[i] = clients[len(clients)-1]
s.transactions = clients[:len(clients)-1]
close(ch)
break
}
}
s.mu.Unlock()
}()
}
// SpentnessNotifications is a notification that is fired for transaction
// outputs controlled by some account's keys. The notification may be about a
// newly added unspent transaction output or that a previously unspent output is
// now spent. When spent, the notification includes the spending transaction's
// hash and input index.
type SpentnessNotifications struct {
hash *chainhash.Hash
spenderHash *chainhash.Hash
index uint32
spenderIndex uint32
}
// Hash returns the transaction hash of the spent output.
func (n *SpentnessNotifications) Hash() *chainhash.Hash {
return n.hash
}
// Index returns the transaction output index of the spent output.
func (n *SpentnessNotifications) Index() uint32 {
return n.index
}
// Spender returns the spending transction's hash and input index, if any. If
// the output is unspent, the final bool return is false.
func (n *SpentnessNotifications) Spender() (*chainhash.Hash, uint32, bool) {
return n.spenderHash, n.spenderIndex, n.spenderHash != nil
}
// notifyUnspentOutput notifies registered clients of a new unspent output that
// is controlled by the wallet.
func (s *NotificationServer) notifyUnspentOutput(account uint32, hash *chainhash.Hash, index uint32) {
defer s.mu.Unlock()
s.mu.Lock()
clients := s.spentness[account]
if len(clients) == 0 {
return
}
n := &SpentnessNotifications{
hash: hash,
index: index,
}
for _, c := range clients {
c <- n
}
}
// notifySpentOutput notifies registered clients that a previously-unspent
// output is now spent, and includes the spender hash and input index in the
// notification.
func (s *NotificationServer) notifySpentOutput(account uint32, op *wire.OutPoint, spenderHash *chainhash.Hash, spenderIndex uint32) {
defer s.mu.Unlock()
s.mu.Lock()
clients := s.spentness[account]
if len(clients) == 0 {
return
}
n := &SpentnessNotifications{
hash: &op.Hash,
index: op.Index,
spenderHash: spenderHash,
spenderIndex: spenderIndex,
}
for _, c := range clients {
c <- n
}
}
// SpentnessNotificationsClient receives SpentnessNotifications from the
// NotificationServer over the channel C.
type SpentnessNotificationsClient struct {
C <-chan *SpentnessNotifications
account uint32
server *NotificationServer
}
// AccountSpentnessNotifications registers a client for spentness changes of
// outputs controlled by the account.
func (s *NotificationServer) AccountSpentnessNotifications(account uint32) SpentnessNotificationsClient {
c := make(chan *SpentnessNotifications)
s.mu.Lock()
s.spentness[account] = append(s.spentness[account], c)
s.mu.Unlock()
return SpentnessNotificationsClient{
C: c,
account: account,
server: s,
}
}
// Done deregisters the client from the server and drains any remaining
// messages. It must be called exactly once when the client is finished
// receiving notifications.
func (c *SpentnessNotificationsClient) Done() {
go func() {
// Drain notifications until the client channel is removed from
// the server and closed.
for range c.C {
}
}()
go func() {
s := c.server
s.mu.Lock()
clients := s.spentness[c.account]
for i, ch := range clients {
if c.C == ch {
clients[i] = clients[len(clients)-1]
s.spentness[c.account] = clients[:len(clients)-1]
close(ch)
break
}
}
s.mu.Unlock()
}()
}
// AccountNotification contains properties regarding an account, such as its
// name and the number of derived and imported keys. When any of these
// properties change, the notification is fired.
type AccountNotification struct {
AccountNumber uint32
AccountName string
ExternalKeyCount uint32
InternalKeyCount uint32
ImportedKeyCount uint32
}
func (s *NotificationServer) notifyAccountProperties(props *waddrmgr.AccountProperties) {
defer s.mu.Unlock()
s.mu.Lock()
clients := s.accountClients
if len(clients) == 0 {
return
}
n := &AccountNotification{
AccountNumber: props.AccountNumber,
AccountName: props.AccountName,
ExternalKeyCount: props.ExternalKeyCount,
InternalKeyCount: props.InternalKeyCount,
ImportedKeyCount: props.ImportedKeyCount,
}
for _, c := range clients {
c <- n
}
}
// AccountNotificationsClient receives AccountNotifications over the channel C.
type AccountNotificationsClient struct {
C chan *AccountNotification
server *NotificationServer
}
// AccountNotifications returns a client for receiving AccountNotifications over
// a channel. The channel is unbuffered. When finished, the client's Done
// method should be called to disassociate the client from the server.
func (s *NotificationServer) AccountNotifications() AccountNotificationsClient {
c := make(chan *AccountNotification)
s.mu.Lock()
s.accountClients = append(s.accountClients, c)
s.mu.Unlock()
return AccountNotificationsClient{
C: c,
server: s,
}
}
// Done deregisters the client from the server and drains any remaining
// messages. It must be called exactly once when the client is finished
// receiving notifications.
func (c *AccountNotificationsClient) Done() {
go func() {
for range c.C {
}
}()
go func() {
s := c.server
s.mu.Lock()
clients := s.accountClients
for i, ch := range clients {
if c.C == ch {
clients[i] = clients[len(clients)-1]
s.accountClients = clients[:len(clients)-1]
close(ch)
break
}
}
s.mu.Unlock()
}()
}

410
vendor/github.com/btcsuite/btcwallet/wallet/recovery.go generated vendored Normal file
View File

@ -0,0 +1,410 @@
package wallet
import (
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/walletdb"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// RecoveryManager maintains the state required to recover previously used
// addresses, and coordinates batched processing of the blocks to search.
type RecoveryManager struct {
// recoveryWindow defines the key-derivation lookahead used when
// attempting to recover the set of used addresses.
recoveryWindow uint32
// started is true after the first block has been added to the batch.
started bool
// blockBatch contains a list of blocks that have not yet been searched
// for recovered addresses.
blockBatch []wtxmgr.BlockMeta
// state encapsulates and allocates the necessary recovery state for all
// key scopes and subsidiary derivation paths.
state *RecoveryState
// chainParams are the parameters that describe the chain we're trying
// to recover funds on.
chainParams *chaincfg.Params
}
// NewRecoveryManager initializes a new RecoveryManager with a derivation
// look-ahead of `recoveryWindow` child indexes, and pre-allocates a backing
// array for `batchSize` blocks to scan at once.
func NewRecoveryManager(recoveryWindow, batchSize uint32,
chainParams *chaincfg.Params) *RecoveryManager {
return &RecoveryManager{
recoveryWindow: recoveryWindow,
blockBatch: make([]wtxmgr.BlockMeta, 0, batchSize),
chainParams: chainParams,
state: NewRecoveryState(recoveryWindow),
}
}
// Resurrect restores all known addresses for the provided scopes that can be
// found in the walletdb namespace, in addition to restoring all outpoints that
// have been previously found. This method ensures that the recovery state's
// horizons properly start from the last found address of a prior recovery
// attempt.
func (rm *RecoveryManager) Resurrect(ns walletdb.ReadBucket,
scopedMgrs map[waddrmgr.KeyScope]*waddrmgr.ScopedKeyManager,
credits []wtxmgr.Credit) error {
// First, for each scope that we are recovering, rederive all of the
// addresses up to the last found address known to each branch.
for keyScope, scopedMgr := range scopedMgrs {
// Load the current account properties for this scope, using the
// the default account number.
// TODO(conner): rescan for all created accounts if we allow
// users to use non-default address
scopeState := rm.state.StateForScope(keyScope)
acctProperties, err := scopedMgr.AccountProperties(
ns, waddrmgr.DefaultAccountNum,
)
if err != nil {
return err
}
// Fetch the external key count, which bounds the indexes we
// will need to rederive.
externalCount := acctProperties.ExternalKeyCount
// Walk through all indexes through the last external key,
// deriving each address and adding it to the external branch
// recovery state's set of addresses to look for.
for i := uint32(0); i < externalCount; i++ {
keyPath := externalKeyPath(i)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
if err != nil && err != hdkeychain.ErrInvalidChild {
return err
} else if err == hdkeychain.ErrInvalidChild {
scopeState.ExternalBranch.MarkInvalidChild(i)
continue
}
scopeState.ExternalBranch.AddAddr(i, addr.Address())
}
// Fetch the internal key count, which bounds the indexes we
// will need to rederive.
internalCount := acctProperties.InternalKeyCount
// Walk through all indexes through the last internal key,
// deriving each address and adding it to the internal branch
// recovery state's set of addresses to look for.
for i := uint32(0); i < internalCount; i++ {
keyPath := internalKeyPath(i)
addr, err := scopedMgr.DeriveFromKeyPath(ns, keyPath)
if err != nil && err != hdkeychain.ErrInvalidChild {
return err
} else if err == hdkeychain.ErrInvalidChild {
scopeState.InternalBranch.MarkInvalidChild(i)
continue
}
scopeState.InternalBranch.AddAddr(i, addr.Address())
}
// The key counts will point to the next key that can be
// derived, so we subtract one to point to last known key. If
// the key count is zero, then no addresses have been found.
if externalCount > 0 {
scopeState.ExternalBranch.ReportFound(externalCount - 1)
}
if internalCount > 0 {
scopeState.InternalBranch.ReportFound(internalCount - 1)
}
}
// In addition, we will re-add any outpoints that are known the wallet
// to our global set of watched outpoints, so that we can watch them for
// spends.
for _, credit := range credits {
_, addrs, _, err := txscript.ExtractPkScriptAddrs(
credit.PkScript, rm.chainParams,
)
if err != nil {
return err
}
rm.state.AddWatchedOutPoint(&credit.OutPoint, addrs[0])
}
return nil
}
// AddToBlockBatch appends the block information, consisting of hash and height,
// to the batch of blocks to be searched.
func (rm *RecoveryManager) AddToBlockBatch(hash *chainhash.Hash, height int32,
timestamp time.Time) {
if !rm.started {
log.Infof("Seed birthday surpassed, starting recovery "+
"of wallet from height=%d hash=%v with "+
"recovery-window=%d", height, *hash, rm.recoveryWindow)
rm.started = true
}
block := wtxmgr.BlockMeta{
Block: wtxmgr.Block{
Hash: *hash,
Height: height,
},
Time: timestamp,
}
rm.blockBatch = append(rm.blockBatch, block)
}
// BlockBatch returns a buffer of blocks that have not yet been searched.
func (rm *RecoveryManager) BlockBatch() []wtxmgr.BlockMeta {
return rm.blockBatch
}
// ResetBlockBatch resets the internal block buffer to conserve memory.
func (rm *RecoveryManager) ResetBlockBatch() {
rm.blockBatch = rm.blockBatch[:0]
}
// State returns the current RecoveryState.
func (rm *RecoveryManager) State() *RecoveryState {
return rm.state
}
// RecoveryState manages the initialization and lookup of ScopeRecoveryStates
// for any actively used key scopes.
//
// In order to ensure that all addresses are properly recovered, the window
// should be sized as the sum of maximum possible inter-block and intra-block
// gap between used addresses of a particular branch.
//
// These are defined as:
// - Inter-Block Gap: The maximum difference between the derived child indexes
// of the last addresses used in any block and the next address consumed
// by a later block.
// - Intra-Block Gap: The maximum difference between the derived child indexes
// of the first address used in any block and the last address used in the
// same block.
type RecoveryState struct {
// recoveryWindow defines the key-derivation lookahead used when
// attempting to recover the set of used addresses. This value will be
// used to instantiate a new RecoveryState for each requested scope.
recoveryWindow uint32
// scopes maintains a map of each requested key scope to its active
// RecoveryState.
scopes map[waddrmgr.KeyScope]*ScopeRecoveryState
// watchedOutPoints contains the set of all outpoints known to the
// wallet. This is updated iteratively as new outpoints are found during
// a rescan.
watchedOutPoints map[wire.OutPoint]btcutil.Address
}
// NewRecoveryState creates a new RecoveryState using the provided
// recoveryWindow. Each RecoveryState that is subsequently initialized for a
// particular key scope will receive the same recoveryWindow.
func NewRecoveryState(recoveryWindow uint32) *RecoveryState {
scopes := make(map[waddrmgr.KeyScope]*ScopeRecoveryState)
return &RecoveryState{
recoveryWindow: recoveryWindow,
scopes: scopes,
watchedOutPoints: make(map[wire.OutPoint]btcutil.Address),
}
}
// StateForScope returns a ScopeRecoveryState for the provided key scope. If one
// does not already exist, a new one will be generated with the RecoveryState's
// recoveryWindow.
func (rs *RecoveryState) StateForScope(
keyScope waddrmgr.KeyScope) *ScopeRecoveryState {
// If the account recovery state already exists, return it.
if scopeState, ok := rs.scopes[keyScope]; ok {
return scopeState
}
// Otherwise, initialize the recovery state for this scope with the
// chosen recovery window.
rs.scopes[keyScope] = NewScopeRecoveryState(rs.recoveryWindow)
return rs.scopes[keyScope]
}
// WatchedOutPoints returns the global set of outpoints that are known to belong
// to the wallet during recovery.
func (rs *RecoveryState) WatchedOutPoints() map[wire.OutPoint]btcutil.Address {
return rs.watchedOutPoints
}
// AddWatchedOutPoint updates the recovery state's set of known outpoints that
// we will monitor for spends during recovery.
func (rs *RecoveryState) AddWatchedOutPoint(outPoint *wire.OutPoint,
addr btcutil.Address) {
rs.watchedOutPoints[*outPoint] = addr
}
// ScopeRecoveryState is used to manage the recovery of addresses generated
// under a particular BIP32 account. Each account tracks both an external and
// internal branch recovery state, both of which use the same recovery window.
type ScopeRecoveryState struct {
// ExternalBranch is the recovery state of addresses generated for
// external use, i.e. receiving addresses.
ExternalBranch *BranchRecoveryState
// InternalBranch is the recovery state of addresses generated for
// internal use, i.e. change addresses.
InternalBranch *BranchRecoveryState
}
// NewScopeRecoveryState initializes an ScopeRecoveryState with the chosen
// recovery window.
func NewScopeRecoveryState(recoveryWindow uint32) *ScopeRecoveryState {
return &ScopeRecoveryState{
ExternalBranch: NewBranchRecoveryState(recoveryWindow),
InternalBranch: NewBranchRecoveryState(recoveryWindow),
}
}
// BranchRecoveryState maintains the required state in-order to properly
// recover addresses derived from a particular account's internal or external
// derivation branch.
//
// A branch recovery state supports operations for:
// - Expanding the look-ahead horizon based on which indexes have been found.
// - Registering derived addresses with indexes within the horizon.
// - Reporting an invalid child index that falls into the horizon.
// - Reporting that an address has been found.
// - Retrieving all currently derived addresses for the branch.
// - Looking up a particular address by its child index.
type BranchRecoveryState struct {
// recoveryWindow defines the key-derivation lookahead used when
// attempting to recover the set of addresses on this branch.
recoveryWindow uint32
// horizion records the highest child index watched by this branch.
horizon uint32
// nextUnfound maintains the child index of the successor to the highest
// index that has been found during recovery of this branch.
nextUnfound uint32
// addresses is a map of child index to address for all actively watched
// addresses belonging to this branch.
addresses map[uint32]btcutil.Address
// invalidChildren records the set of child indexes that derive to
// invalid keys.
invalidChildren map[uint32]struct{}
}
// NewBranchRecoveryState creates a new BranchRecoveryState that can be used to
// track either the external or internal branch of an account's derivation path.
func NewBranchRecoveryState(recoveryWindow uint32) *BranchRecoveryState {
return &BranchRecoveryState{
recoveryWindow: recoveryWindow,
addresses: make(map[uint32]btcutil.Address),
invalidChildren: make(map[uint32]struct{}),
}
}
// ExtendHorizon returns the current horizon and the number of addresses that
// must be derived in order to maintain the desired recovery window.
func (brs *BranchRecoveryState) ExtendHorizon() (uint32, uint32) {
// Compute the new horizon, which should surpass our last found address
// by the recovery window.
curHorizon := brs.horizon
nInvalid := brs.NumInvalidInHorizon()
minValidHorizon := brs.nextUnfound + brs.recoveryWindow + nInvalid
// If the current horizon is sufficient, we will not have to derive any
// new keys.
if curHorizon >= minValidHorizon {
return curHorizon, 0
}
// Otherwise, the number of addresses we should derive corresponds to
// the delta of the two horizons, and we update our new horizon.
delta := minValidHorizon - curHorizon
brs.horizon = minValidHorizon
return curHorizon, delta
}
// AddAddr adds a freshly derived address from our lookahead into the map of
// known addresses for this branch.
func (brs *BranchRecoveryState) AddAddr(index uint32, addr btcutil.Address) {
brs.addresses[index] = addr
}
// GetAddr returns the address derived from a given child index.
func (brs *BranchRecoveryState) GetAddr(index uint32) btcutil.Address {
return brs.addresses[index]
}
// ReportFound updates the last found index if the reported index exceeds the
// current value.
func (brs *BranchRecoveryState) ReportFound(index uint32) {
if index >= brs.nextUnfound {
brs.nextUnfound = index + 1
// Prune all invalid child indexes that fall below our last
// found index. We don't need to keep these entries any longer,
// since they will not affect our required look-ahead.
for childIndex := range brs.invalidChildren {
if childIndex < index {
delete(brs.invalidChildren, childIndex)
}
}
}
}
// MarkInvalidChild records that a particular child index results in deriving an
// invalid address. In addition, the branch's horizon is increment, as we expect
// the caller to perform an additional derivation to replace the invalid child.
// This is used to ensure that we are always have the proper lookahead when an
// invalid child is encountered.
func (brs *BranchRecoveryState) MarkInvalidChild(index uint32) {
brs.invalidChildren[index] = struct{}{}
brs.horizon++
}
// NextUnfound returns the child index of the successor to the highest found
// child index.
func (brs *BranchRecoveryState) NextUnfound() uint32 {
return brs.nextUnfound
}
// Addrs returns a map of all currently derived child indexes to the their
// corresponding addresses.
func (brs *BranchRecoveryState) Addrs() map[uint32]btcutil.Address {
return brs.addresses
}
// NumInvalidInHorizon computes the number of invalid child indexes that lie
// between the last found and current horizon. This informs how many additional
// indexes to derive in order to maintain the proper number of valid addresses
// within our horizon.
func (brs *BranchRecoveryState) NumInvalidInHorizon() uint32 {
var nInvalid uint32
for childIndex := range brs.invalidChildren {
if brs.nextUnfound <= childIndex && childIndex < brs.horizon {
nInvalid++
}
}
return nInvalid
}

318
vendor/github.com/btcsuite/btcwallet/wallet/rescan.go generated vendored Normal file
View File

@ -0,0 +1,318 @@
// 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 wallet
import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/chain"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wtxmgr"
)
// RescanProgressMsg reports the current progress made by a rescan for a
// set of wallet addresses.
type RescanProgressMsg struct {
Addresses []btcutil.Address
Notification *chain.RescanProgress
}
// RescanFinishedMsg reports the addresses that were rescanned when a
// rescanfinished message was received rescanning a batch of addresses.
type RescanFinishedMsg struct {
Addresses []btcutil.Address
Notification *chain.RescanFinished
}
// RescanJob is a job to be processed by the RescanManager. The job includes
// a set of wallet addresses, a starting height to begin the rescan, and
// outpoints spendable by the addresses thought to be unspent. After the
// rescan completes, the error result of the rescan RPC is sent on the Err
// channel.
type RescanJob struct {
InitialSync bool
Addrs []btcutil.Address
OutPoints map[wire.OutPoint]btcutil.Address
BlockStamp waddrmgr.BlockStamp
err chan error
}
// rescanBatch is a collection of one or more RescanJobs that were merged
// together before a rescan is performed.
type rescanBatch struct {
initialSync bool
addrs []btcutil.Address
outpoints map[wire.OutPoint]btcutil.Address
bs waddrmgr.BlockStamp
errChans []chan error
}
// SubmitRescan submits a RescanJob to the RescanManager. A channel is
// returned with the final error of the rescan. The channel is buffered
// and does not need to be read to prevent a deadlock.
func (w *Wallet) SubmitRescan(job *RescanJob) <-chan error {
errChan := make(chan error, 1)
job.err = errChan
select {
case w.rescanAddJob <- job:
case <-w.quitChan():
errChan <- ErrWalletShuttingDown
}
return errChan
}
// batch creates the rescanBatch for a single rescan job.
func (job *RescanJob) batch() *rescanBatch {
return &rescanBatch{
initialSync: job.InitialSync,
addrs: job.Addrs,
outpoints: job.OutPoints,
bs: job.BlockStamp,
errChans: []chan error{job.err},
}
}
// merge merges the work from k into j, setting the starting height to
// the minimum of the two jobs. This method does not check for
// duplicate addresses or outpoints.
func (b *rescanBatch) merge(job *RescanJob) {
if job.InitialSync {
b.initialSync = true
}
b.addrs = append(b.addrs, job.Addrs...)
for op, addr := range job.OutPoints {
b.outpoints[op] = addr
}
if job.BlockStamp.Height < b.bs.Height {
b.bs = job.BlockStamp
}
b.errChans = append(b.errChans, job.err)
}
// done iterates through all error channels, duplicating sending the error
// to inform callers that the rescan finished (or could not complete due
// to an error).
func (b *rescanBatch) done(err error) {
for _, c := range b.errChans {
c <- err
}
}
// rescanBatchHandler handles incoming rescan request, serializing rescan
// submissions, and possibly batching many waiting requests together so they
// can be handled by a single rescan after the current one completes.
func (w *Wallet) rescanBatchHandler() {
defer w.wg.Done()
var curBatch, nextBatch *rescanBatch
quit := w.quitChan()
for {
select {
case job := <-w.rescanAddJob:
if curBatch == nil {
// Set current batch as this job and send
// request.
curBatch = job.batch()
select {
case w.rescanBatch <- curBatch:
case <-quit:
job.err <- ErrWalletShuttingDown
return
}
} else {
// Create next batch if it doesn't exist, or
// merge the job.
if nextBatch == nil {
nextBatch = job.batch()
} else {
nextBatch.merge(job)
}
}
case n := <-w.rescanNotifications:
switch n := n.(type) {
case *chain.RescanProgress:
if curBatch == nil {
log.Warnf("Received rescan progress " +
"notification but no rescan " +
"currently running")
continue
}
select {
case w.rescanProgress <- &RescanProgressMsg{
Addresses: curBatch.addrs,
Notification: n,
}:
case <-quit:
for _, errChan := range curBatch.errChans {
errChan <- ErrWalletShuttingDown
}
return
}
case *chain.RescanFinished:
if curBatch == nil {
log.Warnf("Received rescan finished " +
"notification but no rescan " +
"currently running")
continue
}
select {
case w.rescanFinished <- &RescanFinishedMsg{
Addresses: curBatch.addrs,
Notification: n,
}:
case <-quit:
for _, errChan := range curBatch.errChans {
errChan <- ErrWalletShuttingDown
}
return
}
curBatch, nextBatch = nextBatch, nil
if curBatch != nil {
select {
case w.rescanBatch <- curBatch:
case <-quit:
for _, errChan := range curBatch.errChans {
errChan <- ErrWalletShuttingDown
}
return
}
}
default:
// Unexpected message
panic(n)
}
case <-quit:
return
}
}
}
// rescanProgressHandler handles notifications for partially and fully completed
// rescans by marking each rescanned address as partially or fully synced.
func (w *Wallet) rescanProgressHandler() {
quit := w.quitChan()
out:
for {
// These can't be processed out of order since both chans are
// unbuffured and are sent from same context (the batch
// handler).
select {
case msg := <-w.rescanProgress:
n := msg.Notification
log.Infof("Rescanned through block %v (height %d)",
n.Hash, n.Height)
case msg := <-w.rescanFinished:
n := msg.Notification
addrs := msg.Addresses
noun := pickNoun(len(addrs), "address", "addresses")
log.Infof("Finished rescan for %d %s (synced to block "+
"%s, height %d)", len(addrs), noun, n.Hash,
n.Height)
go w.resendUnminedTxs()
case <-quit:
break out
}
}
w.wg.Done()
}
// rescanRPCHandler reads batch jobs sent by rescanBatchHandler and sends the
// RPC requests to perform a rescan. New jobs are not read until a rescan
// finishes.
func (w *Wallet) rescanRPCHandler() {
chainClient, err := w.requireChainClient()
if err != nil {
log.Errorf("rescanRPCHandler called without an RPC client")
w.wg.Done()
return
}
quit := w.quitChan()
out:
for {
select {
case batch := <-w.rescanBatch:
// Log the newly-started rescan.
numAddrs := len(batch.addrs)
noun := pickNoun(numAddrs, "address", "addresses")
log.Infof("Started rescan from block %v (height %d) for %d %s",
batch.bs.Hash, batch.bs.Height, numAddrs, noun)
err := chainClient.Rescan(&batch.bs.Hash, batch.addrs,
batch.outpoints)
if err != nil {
log.Errorf("Rescan for %d %s failed: %v", numAddrs,
noun, err)
}
batch.done(err)
case <-quit:
break out
}
}
w.wg.Done()
}
// Rescan begins a rescan for all active addresses and unspent outputs of
// a wallet. This is intended to be used to sync a wallet back up to the
// current best block in the main chain, and is considered an initial sync
// rescan.
func (w *Wallet) Rescan(addrs []btcutil.Address, unspent []wtxmgr.Credit) error {
return w.rescanWithTarget(addrs, unspent, nil)
}
// rescanWithTarget performs a rescan starting at the optional startStamp. If
// none is provided, the rescan will begin from the manager's sync tip.
func (w *Wallet) rescanWithTarget(addrs []btcutil.Address,
unspent []wtxmgr.Credit, startStamp *waddrmgr.BlockStamp) error {
outpoints := make(map[wire.OutPoint]btcutil.Address, len(unspent))
for _, output := range unspent {
_, outputAddrs, _, err := txscript.ExtractPkScriptAddrs(
output.PkScript, w.chainParams,
)
if err != nil {
return err
}
outpoints[output.OutPoint] = outputAddrs[0]
}
// If a start block stamp was provided, we will use that as the initial
// starting point for the rescan.
if startStamp == nil {
startStamp = &waddrmgr.BlockStamp{}
*startStamp = w.Manager.SyncedTo()
}
job := &RescanJob{
InitialSync: true,
Addrs: addrs,
OutPoints: outpoints,
BlockStamp: *startStamp,
}
// Submit merged job and block until rescan completes.
select {
case err := <-w.SubmitRescan(job):
return err
case <-w.quitChan():
return ErrWalletShuttingDown
}
}

View File

@ -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.

View File

@ -0,0 +1,367 @@
// 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 txauthor provides transaction creation code for wallets.
package txauthor
import (
"errors"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcwallet/wallet/txrules"
"github.com/btcsuite/btcwallet/wallet/txsizes"
)
// SumOutputValues sums up the list of TxOuts and returns an Amount.
func SumOutputValues(outputs []*wire.TxOut) (totalOutput btcutil.Amount) {
for _, txOut := range outputs {
totalOutput += btcutil.Amount(txOut.Value)
}
return totalOutput
}
// InputSource provides transaction inputs referencing spendable outputs to
// construct a transaction outputting some target amount. If the target amount
// can not be satisified, this can be signaled by returning a total amount less
// than the target or by returning a more detailed error implementing
// InputSourceError.
type InputSource func(target btcutil.Amount) (total btcutil.Amount, inputs []*wire.TxIn,
inputValues []btcutil.Amount, scripts [][]byte, err error)
// InputSourceError describes the failure to provide enough input value from
// unspent transaction outputs to meet a target amount. A typed error is used
// so input sources can provide their own implementations describing the reason
// for the error, for example, due to spendable policies or locked coins rather
// than the wallet not having enough available input value.
type InputSourceError interface {
error
InputSourceError()
}
// Default implementation of InputSourceError.
type insufficientFundsError struct{}
func (insufficientFundsError) InputSourceError() {}
func (insufficientFundsError) Error() string {
return "insufficient funds available to construct transaction"
}
// AuthoredTx holds the state of a newly-created transaction and the change
// output (if one was added).
type AuthoredTx struct {
Tx *wire.MsgTx
PrevScripts [][]byte
PrevInputValues []btcutil.Amount
TotalInput btcutil.Amount
ChangeIndex int // negative if no change
}
// ChangeSource provides P2PKH change output scripts for transaction creation.
type ChangeSource func() ([]byte, error)
// NewUnsignedTransaction creates an unsigned transaction paying to one or more
// non-change outputs. An appropriate transaction fee is included based on the
// transaction size.
//
// Transaction inputs are chosen from repeated calls to fetchInputs with
// increasing targets amounts.
//
// If any remaining output value can be returned to the wallet via a change
// output without violating mempool dust rules, a P2WPKH change output is
// appended to the transaction outputs. Since the change output may not be
// necessary, fetchChange is called zero or one times to generate this script.
// This function must return a P2WPKH script or smaller, otherwise fee estimation
// will be incorrect.
//
// If successful, the transaction, total input value spent, and all previous
// output scripts are returned. If the input source was unable to provide
// enough input value to pay for every output any any necessary fees, an
// InputSourceError is returned.
//
// BUGS: Fee estimation may be off when redeeming non-compressed P2PKH outputs.
func NewUnsignedTransaction(outputs []*wire.TxOut, relayFeePerKb btcutil.Amount,
fetchInputs InputSource, fetchChange ChangeSource) (*AuthoredTx, error) {
targetAmount := SumOutputValues(outputs)
estimatedSize := txsizes.EstimateVirtualSize(0, 1, 0, outputs, true)
targetFee := txrules.FeeForSerializeSize(relayFeePerKb, estimatedSize)
for {
inputAmount, inputs, inputValues, scripts, err := fetchInputs(targetAmount + targetFee)
if err != nil {
return nil, err
}
if inputAmount < targetAmount+targetFee {
return nil, insufficientFundsError{}
}
// We count the types of inputs, which we'll use to estimate
// the vsize of the transaction.
var nested, p2wpkh, p2pkh int
for _, pkScript := range scripts {
switch {
// If this is a p2sh output, we assume this is a
// nested P2WKH.
case txscript.IsPayToScriptHash(pkScript):
nested++
case txscript.IsPayToWitnessPubKeyHash(pkScript):
p2wpkh++
default:
p2pkh++
}
}
maxSignedSize := txsizes.EstimateVirtualSize(p2pkh, p2wpkh,
nested, outputs, true)
maxRequiredFee := txrules.FeeForSerializeSize(relayFeePerKb, maxSignedSize)
remainingAmount := inputAmount - targetAmount
if remainingAmount < maxRequiredFee {
targetFee = maxRequiredFee
continue
}
unsignedTransaction := &wire.MsgTx{
Version: wire.TxVersion,
TxIn: inputs,
TxOut: outputs,
LockTime: 0,
}
changeIndex := -1
changeAmount := inputAmount - targetAmount - maxRequiredFee
if changeAmount != 0 && !txrules.IsDustAmount(changeAmount,
txsizes.P2WPKHPkScriptSize, relayFeePerKb) {
changeScript, err := fetchChange()
if err != nil {
return nil, err
}
if len(changeScript) > txsizes.P2WPKHPkScriptSize {
return nil, errors.New("fee estimation requires change " +
"scripts no larger than P2WPKH output scripts")
}
change := wire.NewTxOut(int64(changeAmount), changeScript)
l := len(outputs)
unsignedTransaction.TxOut = append(outputs[:l:l], change)
changeIndex = l
}
return &AuthoredTx{
Tx: unsignedTransaction,
PrevScripts: scripts,
PrevInputValues: inputValues,
TotalInput: inputAmount,
ChangeIndex: changeIndex,
}, nil
}
}
// RandomizeOutputPosition randomizes the position of a transaction's output by
// swapping it with a random output. The new index is returned. This should be
// done before signing.
func RandomizeOutputPosition(outputs []*wire.TxOut, index int) int {
r := cprng.Int31n(int32(len(outputs)))
outputs[r], outputs[index] = outputs[index], outputs[r]
return int(r)
}
// RandomizeChangePosition randomizes the position of an authored transaction's
// change output. This should be done before signing.
func (tx *AuthoredTx) RandomizeChangePosition() {
tx.ChangeIndex = RandomizeOutputPosition(tx.Tx.TxOut, tx.ChangeIndex)
}
// SecretsSource provides private keys and redeem scripts necessary for
// constructing transaction input signatures. Secrets are looked up by the
// corresponding Address for the previous output script. Addresses for lookup
// are created using the source's blockchain parameters and means a single
// SecretsSource can only manage secrets for a single chain.
//
// TODO: Rewrite this interface to look up private keys and redeem scripts for
// pubkeys, pubkey hashes, script hashes, etc. as separate interface methods.
// This would remove the ChainParams requirement of the interface and could
// avoid unnecessary conversions from previous output scripts to Addresses.
// This can not be done without modifications to the txscript package.
type SecretsSource interface {
txscript.KeyDB
txscript.ScriptDB
ChainParams() *chaincfg.Params
}
// AddAllInputScripts modifies transaction a transaction by adding inputs
// scripts for each input. Previous output scripts being redeemed by each input
// are passed in prevPkScripts and the slice length must match the number of
// inputs. Private keys and redeem scripts are looked up using a SecretsSource
// based on the previous output script.
func AddAllInputScripts(tx *wire.MsgTx, prevPkScripts [][]byte, inputValues []btcutil.Amount,
secrets SecretsSource) error {
inputs := tx.TxIn
hashCache := txscript.NewTxSigHashes(tx)
chainParams := secrets.ChainParams()
if len(inputs) != len(prevPkScripts) {
return errors.New("tx.TxIn and prevPkScripts slices must " +
"have equal length")
}
for i := range inputs {
pkScript := prevPkScripts[i]
switch {
// If this is a p2sh output, who's script hash pre-image is a
// witness program, then we'll need to use a modified signing
// function which generates both the sigScript, and the witness
// script.
case txscript.IsPayToScriptHash(pkScript):
err := spendNestedWitnessPubKeyHash(inputs[i], pkScript,
int64(inputValues[i]), chainParams, secrets,
tx, hashCache, i)
if err != nil {
return err
}
case txscript.IsPayToWitnessPubKeyHash(pkScript):
err := spendWitnessKeyHash(inputs[i], pkScript,
int64(inputValues[i]), chainParams, secrets,
tx, hashCache, i)
if err != nil {
return err
}
default:
sigScript := inputs[i].SignatureScript
script, err := txscript.SignTxOutput(chainParams, tx, i,
pkScript, txscript.SigHashAll, secrets, secrets,
sigScript)
if err != nil {
return err
}
inputs[i].SignatureScript = script
}
}
return nil
}
// spendWitnessKeyHash generates, and sets a valid witness for spending the
// passed pkScript with the specified input amount. The input amount *must*
// correspond to the output value of the previous pkScript, or else verification
// will fail since the new sighash digest algorithm defined in BIP0143 includes
// the input value in the sighash.
func spendWitnessKeyHash(txIn *wire.TxIn, pkScript []byte,
inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource,
tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error {
// First obtain the key pair associated with this p2wkh address.
_, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript,
chainParams)
if err != nil {
return err
}
privKey, compressed, err := secrets.GetKey(addrs[0])
if err != nil {
return err
}
pubKey := privKey.PubKey()
// Once we have the key pair, generate a p2wkh address type, respecting
// the compression type of the generated key.
var pubKeyHash []byte
if compressed {
pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed())
} else {
pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed())
}
p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams)
if err != nil {
return err
}
// With the concrete address type, we can now generate the
// corresponding witness program to be used to generate a valid witness
// which will allow us to spend this output.
witnessProgram, err := txscript.PayToAddrScript(p2wkhAddr)
if err != nil {
return err
}
witnessScript, err := txscript.WitnessSignature(tx, hashCache, idx,
inputValue, witnessProgram, txscript.SigHashAll, privKey, true)
if err != nil {
return err
}
txIn.Witness = witnessScript
return nil
}
// spendNestedWitnessPubKey generates both a sigScript, and valid witness for
// spending the passed pkScript with the specified input amount. The generated
// sigScript is the version 0 p2wkh witness program corresponding to the queried
// key. The witness stack is identical to that of one which spends a regular
// p2wkh output. The input amount *must* correspond to the output value of the
// previous pkScript, or else verification will fail since the new sighash
// digest algorithm defined in BIP0143 includes the input value in the sighash.
func spendNestedWitnessPubKeyHash(txIn *wire.TxIn, pkScript []byte,
inputValue int64, chainParams *chaincfg.Params, secrets SecretsSource,
tx *wire.MsgTx, hashCache *txscript.TxSigHashes, idx int) error {
// First we need to obtain the key pair related to this p2sh output.
_, addrs, _, err := txscript.ExtractPkScriptAddrs(pkScript,
chainParams)
if err != nil {
return err
}
privKey, compressed, err := secrets.GetKey(addrs[0])
if err != nil {
return err
}
pubKey := privKey.PubKey()
var pubKeyHash []byte
if compressed {
pubKeyHash = btcutil.Hash160(pubKey.SerializeCompressed())
} else {
pubKeyHash = btcutil.Hash160(pubKey.SerializeUncompressed())
}
// Next, we'll generate a valid sigScript that'll allow us to spend
// the p2sh output. The sigScript will contain only a single push of
// the p2wkh witness program corresponding to the matching public key
// of this address.
p2wkhAddr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainParams)
if err != nil {
return err
}
witnessProgram, err := txscript.PayToAddrScript(p2wkhAddr)
if err != nil {
return err
}
bldr := txscript.NewScriptBuilder()
bldr.AddData(witnessProgram)
sigScript, err := bldr.Script()
if err != nil {
return err
}
txIn.SignatureScript = sigScript
// With the sigScript in place, we'll next generate the proper witness
// that'll allow us to spend the p2wkh output.
witnessScript, err := txscript.WitnessSignature(tx, hashCache, idx,
inputValue, witnessProgram, txscript.SigHashAll, privKey, compressed)
if err != nil {
return err
}
txIn.Witness = witnessScript
return nil
}
// AddAllInputScripts modifies an authored transaction by adding inputs scripts
// for each input of an authored transaction. Private keys and redeem scripts
// are looked up using a SecretsSource based on the previous output script.
func (tx *AuthoredTx) AddAllInputScripts(secrets SecretsSource) error {
return AddAllInputScripts(tx.Tx, tx.PrevScripts, tx.PrevInputValues, secrets)
}

Some files were not shown because too many files have changed in this diff Show More