From 4126760706a04c9e39a6c1ed988e49ba17721da6 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 18 Dec 2023 16:41:58 -0800 Subject: [PATCH 1/5] btcutil/psbt: update to btcutil btcutil/v1.1.4 --- btcutil/psbt/go.mod | 4 ++-- btcutil/psbt/go.sum | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/btcutil/psbt/go.mod b/btcutil/psbt/go.mod index b5c4461d86..81ccca266b 100644 --- a/btcutil/psbt/go.mod +++ b/btcutil/psbt/go.mod @@ -3,9 +3,9 @@ module github.com/btcsuite/btcd/btcutil/psbt go 1.17 require ( - github.com/btcsuite/btcd v0.23.0 + github.com/btcsuite/btcd v0.23.5-0.20231219003633-4c2ce6daed8f github.com/btcsuite/btcd/btcec/v2 v2.1.3 - github.com/btcsuite/btcd/btcutil v1.1.3 + github.com/btcsuite/btcd/btcutil v1.1.4 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/davecgh/go-spew v1.1.1 github.com/stretchr/testify v1.7.0 diff --git a/btcutil/psbt/go.sum b/btcutil/psbt/go.sum index c9d8388f8e..74a2ce8a3d 100644 --- a/btcutil/psbt/go.sum +++ b/btcutil/psbt/go.sum @@ -1,15 +1,16 @@ github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= -github.com/btcsuite/btcd v0.23.0 h1:V2/ZgjfDFIygAX3ZapeigkVBoVUtOJKSwrhZdlpSvaA= -github.com/btcsuite/btcd v0.23.0/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.23.5-0.20231219003633-4c2ce6daed8f h1:E+dQ8sNtK/lOdfeflUKkRLXe/zW7I333C7HhaoASjZA= +github.com/btcsuite/btcd v0.23.5-0.20231219003633-4c2ce6daed8f/go.mod h1:KVEB81PybLGYzpf1db/kKNi1ZEbUsiVGeTGhKuOl5AM= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.4 h1:mWvWRLRIPuoeZsVRpc0xNCkfeNxWy1E4jIZ06ZpGI1A= +github.com/btcsuite/btcd/btcutil v1.1.4/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= @@ -46,6 +47,7 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= From c3c3545f9be953553efdb69d4c4809769c0ce4b5 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 18 Dec 2023 16:53:51 -0800 Subject: [PATCH 2/5] multi: update main package to chainhash/v1.1.0, use optimized dsha256 In this commit, we update the top-level btcd package to use the latest version of btcutil and also the chainhash package. With this version bump, we can now use the new optimized dsha256 routine where applicable. With this commit, I've covered most of the areas we'll hash an entire transaction/block/header, but we may want to optimize some other areas further, in particular, the witness sighash calc. --- blockchain/merkle.go | 6 +++++- go.mod | 4 +--- go.sum | 20 ++++++++++++++++++++ txscript/sighash.go | 25 ++++++++++++++++++++----- wire/blockheader.go | 12 +++--------- wire/msgtx.go | 13 ++----------- 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/blockchain/merkle.go b/blockchain/merkle.go index a1a50a2bd5..b89b518505 100644 --- a/blockchain/merkle.go +++ b/blockchain/merkle.go @@ -7,6 +7,7 @@ package blockchain import ( "bytes" "fmt" + "io" "math" "github.com/btcsuite/btcd/btcutil" @@ -64,7 +65,10 @@ func HashMerkleBranches(left, right *chainhash.Hash) chainhash.Hash { copy(hash[:chainhash.HashSize], left[:]) copy(hash[chainhash.HashSize:], right[:]) - return chainhash.DoubleHashH(hash[:]) + return chainhash.DoubleHashRaw(func(w io.Writer) error { + _, err := w.Write(hash[:]) + return err + }) } // BuildMerkleTreeStore creates a merkle tree from a slice of transactions, diff --git a/go.mod b/go.mod index 977682aae2..36d4aa21bd 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/btcsuite/btcd require ( github.com/btcsuite/btcd/btcec/v2 v2.1.3 - github.com/btcsuite/btcd/btcutil v1.1.0 + github.com/btcsuite/btcd/btcutil v1.1.4 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd @@ -29,8 +29,6 @@ require ( gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) -replace github.com/btcsuite/btcd/btcutil => ./btcutil - // The retract statements below fixes an accidental push of the tags of a btcd // fork. retract ( diff --git a/go.sum b/go.sum index 9944f70ec3..6a2ee39cd8 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,33 @@ 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/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.4 h1:mWvWRLRIPuoeZsVRpc0xNCkfeNxWy1E4jIZ06ZpGI1A= +github.com/btcsuite/btcd/btcutil v1.1.4/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 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 h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= 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/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= 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.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= @@ -42,6 +55,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= @@ -51,9 +65,12 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 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/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -64,9 +81,11 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +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-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -102,6 +121,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/txscript/sighash.go b/txscript/sighash.go index 7dc19ab5bd..486a937765 100644 --- a/txscript/sighash.go +++ b/txscript/sighash.go @@ -169,10 +169,18 @@ func calcSignatureHash(sigScript []byte, hashType SigHashType, tx *wire.MsgTx, i // The final hash is the double sha256 of both the serialized modified // transaction and the hash type (encoded as a 4-byte little-endian // value) appended. - wbuf := bytes.NewBuffer(make([]byte, 0, txCopy.SerializeSizeStripped()+4)) - txCopy.SerializeNoWitness(wbuf) - binary.Write(wbuf, binary.LittleEndian, hashType) - return chainhash.DoubleHashB(wbuf.Bytes()) + sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { + if err := txCopy.SerializeNoWitness(w); err != nil { + return err + } + err := binary.Write(w, binary.LittleEndian, hashType) + if err != nil { + return err + } + return nil + }) + + return sigHashBytes[:] } // calcWitnessSignatureHashRaw computes the sighash digest of a transaction's @@ -289,7 +297,14 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) sigHash.Write(bHashType[:]) - return chainhash.DoubleHashB(sigHash.Bytes()), nil + sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { + // TODO(rosabeef): put entire calc func into this? then no + // intermediate buffer + _, err := sigHash.WriteTo(w) + return err + }) + + return sigHashBytes[:], nil } // CalcWitnessSigHash computes the sighash digest for the specified input of diff --git a/wire/blockheader.go b/wire/blockheader.go index 9c9c2237e6..7af9a3788e 100644 --- a/wire/blockheader.go +++ b/wire/blockheader.go @@ -5,7 +5,6 @@ package wire import ( - "bytes" "io" "time" @@ -46,14 +45,9 @@ const blockHeaderLen = 80 // BlockHash computes the block identifier hash for the given block header. func (h *BlockHeader) BlockHash() chainhash.Hash { - // Encode the header and double sha256 everything prior to the number of - // transactions. Ignore the error returns since there is no way the - // encode could fail except being out of memory which would cause a - // run-time panic. - buf := bytes.NewBuffer(make([]byte, 0, MaxBlockHeaderPayload)) - _ = writeBlockHeader(buf, 0, h) - - return chainhash.DoubleHashH(buf.Bytes()) + return chainhash.DoubleHashRaw(func(w io.Writer) error { + return writeBlockHeader(w, 0, h) + }) } // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. diff --git a/wire/msgtx.go b/wire/msgtx.go index 7705504cc8..d2b52dba24 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -5,7 +5,6 @@ package wire import ( - "bytes" "errors" "fmt" "io" @@ -357,13 +356,7 @@ func (msg *MsgTx) AddTxOut(to *TxOut) { // TxHash generates the Hash for the transaction. func (msg *MsgTx) TxHash() chainhash.Hash { - // Encode the transaction and calculate double sha256 on the result. - // Ignore the error returns since the only way the encode could fail - // is being out of memory or due to nil pointers, both of which would - // cause a run-time panic. - buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSizeStripped())) - _ = msg.SerializeNoWitness(buf) - return chainhash.DoubleHashH(buf.Bytes()) + return chainhash.DoubleHashRaw(msg.SerializeNoWitness) } // WitnessHash generates the hash of the transaction serialized according to @@ -373,9 +366,7 @@ func (msg *MsgTx) TxHash() chainhash.Hash { // is the same as its txid. func (msg *MsgTx) WitnessHash() chainhash.Hash { if msg.HasWitness() { - buf := bytes.NewBuffer(make([]byte, 0, msg.SerializeSize())) - _ = msg.Serialize(buf) - return chainhash.DoubleHashH(buf.Bytes()) + return chainhash.DoubleHashRaw(msg.Serialize) } return msg.TxHash() From adfb641a3626a3cca02a33676c7b1b0ab62c1c2b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Mon, 18 Dec 2023 17:01:02 -0800 Subject: [PATCH 3/5] txscript: use DoubleHashRaw to write directly crypto.Hash for segwit sighash In this commit, we optimize the sighash calc further by writing directly into the buffer used for serialization by the sha256.New() instance rather than to an intermediate buffer, which is then write to the hash buffer. --- txscript/sighash.go | 186 ++++++++++++++++++++++---------------------- 1 file changed, 94 insertions(+), 92 deletions(-) diff --git a/txscript/sighash.go b/txscript/sighash.go index 486a937765..32b776afef 100644 --- a/txscript/sighash.go +++ b/txscript/sighash.go @@ -205,103 +205,105 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, return nil, fmt.Errorf("idx %d but %d txins", idx, len(tx.TxIn)) } - // We'll utilize this buffer throughout to incrementally calculate - // the signature hash for this transaction. - var sigHash bytes.Buffer - - // First write out, then encode the transaction's version number. - var bVersion [4]byte - binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version)) - sigHash.Write(bVersion[:]) - - // Next write out the possibly pre-calculated hashes for the sequence - // numbers of all inputs, and the hashes of the previous outs for all - // outputs. - var zeroHash chainhash.Hash - - // If anyone can pay isn't active, then we can use the cached - // hashPrevOuts, otherwise we just write zeroes for the prev outs. - if hashType&SigHashAnyOneCanPay == 0 { - sigHash.Write(sigHashes.HashPrevOutsV0[:]) - } else { - sigHash.Write(zeroHash[:]) - } + sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { + // First write out, then encode the transaction's version + // number. + var bVersion [4]byte + binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version)) + w.Write(bVersion[:]) + + // Next write out the possibly pre-calculated hashes for the + // sequence numbers of all inputs, and the hashes of the + // previous outs for all outputs. + var zeroHash chainhash.Hash + + // If anyone can pay isn't active, then we can use the cached + // hashPrevOuts, otherwise we just write zeroes for the prev + // outs. + if hashType&SigHashAnyOneCanPay == 0 { + w.Write(sigHashes.HashPrevOutsV0[:]) + } else { + w.Write(zeroHash[:]) + } - // If the sighash isn't anyone can pay, single, or none, the use the - // cached hash sequences, otherwise write all zeroes for the - // hashSequence. - if hashType&SigHashAnyOneCanPay == 0 && - hashType&sigHashMask != SigHashSingle && - hashType&sigHashMask != SigHashNone { - sigHash.Write(sigHashes.HashSequenceV0[:]) - } else { - sigHash.Write(zeroHash[:]) - } + // If the sighash isn't anyone can pay, single, or none, the + // use the cached hash sequences, otherwise write all zeroes + // for the hashSequence. + if hashType&SigHashAnyOneCanPay == 0 && + hashType&sigHashMask != SigHashSingle && + hashType&sigHashMask != SigHashNone { - txIn := tx.TxIn[idx] - - // Next, write the outpoint being spent. - sigHash.Write(txIn.PreviousOutPoint.Hash[:]) - var bIndex [4]byte - binary.LittleEndian.PutUint32(bIndex[:], txIn.PreviousOutPoint.Index) - sigHash.Write(bIndex[:]) - - if isWitnessPubKeyHashScript(subScript) { - // The script code for a p2wkh is a length prefix varint for - // the next 25 bytes, followed by a re-creation of the original - // p2pkh pk script. - sigHash.Write([]byte{0x19}) - sigHash.Write([]byte{OP_DUP}) - sigHash.Write([]byte{OP_HASH160}) - sigHash.Write([]byte{OP_DATA_20}) - sigHash.Write(extractWitnessPubKeyHash(subScript)) - sigHash.Write([]byte{OP_EQUALVERIFY}) - sigHash.Write([]byte{OP_CHECKSIG}) - } else { - // For p2wsh outputs, and future outputs, the script code is - // the original script, with all code separators removed, - // serialized with a var int length prefix. - wire.WriteVarBytes(&sigHash, 0, subScript) - } + w.Write(sigHashes.HashSequenceV0[:]) + } else { + w.Write(zeroHash[:]) + } - // Next, add the input amount, and sequence number of the input being - // signed. - var bAmount [8]byte - binary.LittleEndian.PutUint64(bAmount[:], uint64(amt)) - sigHash.Write(bAmount[:]) - var bSequence [4]byte - binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence) - sigHash.Write(bSequence[:]) - - // If the current signature mode isn't single, or none, then we can - // re-use the pre-generated hashoutputs sighash fragment. Otherwise, - // we'll serialize and add only the target output index to the signature - // pre-image. - if hashType&sigHashMask != SigHashSingle && - hashType&sigHashMask != SigHashNone { - sigHash.Write(sigHashes.HashOutputsV0[:]) - } else if hashType&sigHashMask == SigHashSingle && idx < len(tx.TxOut) { - var b bytes.Buffer - wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx]) - sigHash.Write(chainhash.DoubleHashB(b.Bytes())) - } else { - sigHash.Write(zeroHash[:]) - } + txIn := tx.TxIn[idx] + + // Next, write the outpoint being spent. + w.Write(txIn.PreviousOutPoint.Hash[:]) + var bIndex [4]byte + binary.LittleEndian.PutUint32( + bIndex[:], txIn.PreviousOutPoint.Index, + ) + w.Write(bIndex[:]) + + if isWitnessPubKeyHashScript(subScript) { + // The script code for a p2wkh is a length prefix + // varint for the next 25 bytes, followed by a + // re-creation of the original p2pkh pk script. + w.Write([]byte{0x19}) + w.Write([]byte{OP_DUP}) + w.Write([]byte{OP_HASH160}) + w.Write([]byte{OP_DATA_20}) + w.Write(extractWitnessPubKeyHash(subScript)) + w.Write([]byte{OP_EQUALVERIFY}) + w.Write([]byte{OP_CHECKSIG}) + } else { + // For p2wsh outputs, and future outputs, the script + // code is the original script, with all code + // separators removed, serialized with a var int length + // prefix. + wire.WriteVarBytes(w, 0, subScript) + } - // Finally, write out the transaction's locktime, and the sig hash - // type. - var bLockTime [4]byte - binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime) - sigHash.Write(bLockTime[:]) - var bHashType [4]byte - binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) - sigHash.Write(bHashType[:]) + // Next, add the input amount, and sequence number of the input + // being signed. + var bAmount [8]byte + binary.LittleEndian.PutUint64(bAmount[:], uint64(amt)) + w.Write(bAmount[:]) + var bSequence [4]byte + binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence) + w.Write(bSequence[:]) + + // If the current signature mode isn't single, or none, then we + // can re-use the pre-generated hashoutputs sighash fragment. + // Otherwise, we'll serialize and add only the target output + // index to the signature pre-image. + if hashType&sigHashMask != SigHashSingle && + hashType&sigHashMask != SigHashNone { + + w.Write(sigHashes.HashOutputsV0[:]) + } else if hashType&sigHashMask == SigHashSingle && + idx < len(tx.TxOut) { + + var b bytes.Buffer + wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx]) + w.Write(chainhash.DoubleHashB(b.Bytes())) + } else { + w.Write(zeroHash[:]) + } - sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { - // TODO(rosabeef): put entire calc func into this? then no - // intermediate buffer - _, err := sigHash.WriteTo(w) - return err + // Finally, write out the transaction's locktime, and the sig + // hash type. + var bLockTime [4]byte + binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime) + w.Write(bLockTime[:]) + var bHashType [4]byte + binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) + w.Write(bHashType[:]) + + return nil }) return sigHashBytes[:], nil From 046a70121a6d33d20038d4b9ea5905e0dd29cc1b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 19 Dec 2023 14:40:18 -0800 Subject: [PATCH 4/5] txscript: use DoubleHashRaw for segwit sighash single calc We can write direly into the hash writer vs serializing into a buffer, then writing that into the hash writer. --- txscript/sighash.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/txscript/sighash.go b/txscript/sighash.go index 32b776afef..65a38a1e36 100644 --- a/txscript/sighash.go +++ b/txscript/sighash.go @@ -287,9 +287,11 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, } else if hashType&sigHashMask == SigHashSingle && idx < len(tx.TxOut) { - var b bytes.Buffer - wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx]) - w.Write(chainhash.DoubleHashB(b.Bytes())) + h := chainhash.DoubleHashRaw(func(tw io.Writer) error { + wire.WriteTxOut(tw, 0, 0, tx.TxOut[idx]) + return nil + }) + w.Write(h[:]) } else { w.Write(zeroHash[:]) } From 19008edd0faaa12938895b54aad83af820d703dc Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 19 Dec 2023 14:56:57 -0800 Subject: [PATCH 5/5] txscript: use a single shared scratch buffer in segwit sighash calc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We used to use a lot of small buffers for serialization, but now we'll use one buffer large enough, and slice into it when needed. `` name old time/op new time/op delta CalcWitnessSigHash-8 31.5µs ± 0% 29.2µs ± 0% -7.05% (p=0.000 n=10+10) name old alloc/op new alloc/op delta CalcWitnessSigHash-8 19.9kB ± 0% 18.5kB ± 0% -7.14% (p=0.000 n=10+10) name old allocs/op new allocs/op delta CalcWitnessSigHash-8 801 ± 0% 445 ± 0% -44.44% (p=0.000 n=10+10) ``` --- txscript/sighash.go | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/txscript/sighash.go b/txscript/sighash.go index 65a38a1e36..16c3c19c18 100644 --- a/txscript/sighash.go +++ b/txscript/sighash.go @@ -206,11 +206,12 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, } sigHashBytes := chainhash.DoubleHashRaw(func(w io.Writer) error { + var scratch [8]byte + // First write out, then encode the transaction's version // number. - var bVersion [4]byte - binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version)) - w.Write(bVersion[:]) + binary.LittleEndian.PutUint32(scratch[:], uint32(tx.Version)) + w.Write(scratch[:4]) // Next write out the possibly pre-calculated hashes for the // sequence numbers of all inputs, and the hashes of the @@ -269,12 +270,10 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, // Next, add the input amount, and sequence number of the input // being signed. - var bAmount [8]byte - binary.LittleEndian.PutUint64(bAmount[:], uint64(amt)) - w.Write(bAmount[:]) - var bSequence [4]byte - binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence) - w.Write(bSequence[:]) + binary.LittleEndian.PutUint64(scratch[:], uint64(amt)) + w.Write(scratch[:]) + binary.LittleEndian.PutUint32(scratch[:], txIn.Sequence) + w.Write(scratch[:4]) // If the current signature mode isn't single, or none, then we // can re-use the pre-generated hashoutputs sighash fragment. @@ -298,12 +297,10 @@ func calcWitnessSignatureHashRaw(subScript []byte, sigHashes *TxSigHashes, // Finally, write out the transaction's locktime, and the sig // hash type. - var bLockTime [4]byte - binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime) - w.Write(bLockTime[:]) - var bHashType [4]byte - binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) - w.Write(bHashType[:]) + binary.LittleEndian.PutUint32(scratch[:], tx.LockTime) + w.Write(scratch[:4]) + binary.LittleEndian.PutUint32(scratch[:], uint32(hashType)) + w.Write(scratch[:4]) return nil })