From df74328622004fcfa83b98c0be33d08932483fc0 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Thu, 19 Sep 2019 09:15:33 -0700 Subject: [PATCH 001/148] feature: start the cosmos relay version --- golang/app.go | 66 ++++++++ golang/go.mod | 16 ++ golang/go.sum | 256 ++++++++++++++++++++++++++++++++ golang/x/relay/handler.go | 29 ++++ golang/x/relay/keeper.go | 40 +++++ golang/x/relay/types/codec.go | 17 +++ golang/x/relay/types/errors.go | 130 ++++++++++++++++ golang/x/relay/types/keys.go | 9 ++ golang/x/relay/types/msgs.go | 47 ++++++ golang/x/relay/types/querier.go | 1 + 10 files changed, 611 insertions(+) create mode 100644 golang/app.go create mode 100644 golang/go.mod create mode 100644 golang/go.sum create mode 100644 golang/x/relay/handler.go create mode 100644 golang/x/relay/keeper.go create mode 100644 golang/x/relay/types/codec.go create mode 100644 golang/x/relay/types/errors.go create mode 100644 golang/x/relay/types/keys.go create mode 100644 golang/x/relay/types/msgs.go create mode 100644 golang/x/relay/types/querier.go diff --git a/golang/app.go b/golang/app.go new file mode 100644 index 00000000..81963627 --- /dev/null +++ b/golang/app.go @@ -0,0 +1,66 @@ +package relay + +import ( + "os" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/tendermint/tendermint/libs/log" + + // "github.com/cosmos/cosmos-sdk/x/auth/genaccounts" + // + // "github.com/cosmos/cosmos-sdk/x/bank" + // distr "github.com/cosmos/cosmos-sdk/x/distribution" + // "github.com/cosmos/cosmos-sdk/x/params" + // "github.com/cosmos/cosmos-sdk/x/staking" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + // abci "github.com/tendermint/tendermint/abci/types" + // cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tm-db" +) + +const appName = "summa-cosmos-relay" + +var ( + // DefaultCLIHome is default home directories for the application CLI + DefaultCLIHome = os.ExpandEnv("$HOME/.summa/cosmosrelay") + + // DefaultNodeHome sets the folder where the applcation data and configuration will be stored + DefaultNodeHome = os.ExpandEnv("$HOME/.summa/cosmosrelay/config") + + // ModuleBasics is basic module elemnets + ModuleBasics = module.NewBasicManager() +) + +type relayApp struct { + *bam.BaseApp + cdc *codec.Codec +} + +// MakeCodec generates the necessary codecs for Amino +func MakeCodec() *codec.Codec { + var cdc = codec.New() + ModuleBasics.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + return cdc +} + +// NewRelayApp instantiates a new Bitcoin relay app +func NewRelayApp(logger log.Logger, db dbm.DB) *relayApp { + // First define the top level codec that will be shared by the different modules. Note: Codec will be explained later + cdc := MakeCodec() + + // BaseApp handles interactions with Tendermint through the ABCI protocol + bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)) + + var app = &relayApp{ + BaseApp: bApp, + cdc: cdc, + } + + return app +} diff --git a/golang/go.mod b/golang/go.mod new file mode 100644 index 00000000..9a341406 --- /dev/null +++ b/golang/go.mod @@ -0,0 +1,16 @@ +module github.com/summa-tx/relays/golang + +go 1.12 + +require ( + bou.ke/monkey v1.0.1 // indirect + github.com/cosmos/cosmos-sdk v0.37.0 + github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/gogo/protobuf v1.2.1 + github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1 // indirect + github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 // indirect + github.com/otiai10/mint v1.2.3 // indirect + github.com/summa-tx/bitcoin-spv/golang v1.0.0 + github.com/tendermint/tendermint v0.32.2 + github.com/tendermint/tm-db v0.1.1 +) diff --git a/golang/go.sum b/golang/go.sum new file mode 100644 index 00000000..577f7413 --- /dev/null +++ b/golang/go.sum @@ -0,0 +1,256 @@ +bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4KfQcRcwlmFIrhkDmIj2dz5bkg/s/8= +github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cosmos/cosmos-sdk v0.35.0 h1:EPeie1aKHwnXtTzKggvabG7aAPN+DDmju2xquvjFwao= +github.com/cosmos/cosmos-sdk v0.35.0/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= +github.com/cosmos/cosmos-sdk v0.37.0 h1:S2I3NDGN2wqfGlY5KqkAHTpfezjhgeqDxrCxhlhd528= +github.com/cosmos/cosmos-sdk v0.37.0/go.mod h1:3b/k/Zd+YDuttSmEJdNkxga1H5EIiDUhSYeErAHQN7A= +github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI= +github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +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/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +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 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +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/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/libp2p/go-buffer-pool v0.0.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg= +github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= +github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +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= +github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1/go.mod h1:pXzZSDlN+HPzSdyIBnKNN9ptD9Hx7iZMWIJPTwo4FPE= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +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.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= +github.com/prometheus/common v0.2.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-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs= +github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rakyll/statik v0.1.5 h1:Ly2UjURzxnsSYS0zI50fZ+srA+Fu7EbpV5hglvJvJG0= +github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.0.3 h1:z5LPUc2iz8VLT5Cw1UyrESG6FUUnOGecYGY08BLKSuc= +github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/summa-tx/bitcoin-spv v2.0.0+incompatible h1:so6fH4hvyZ7LI8mC9mLOiV5u+8gxFpsXG84g1dGyK7M= +github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918190112-a7c5660022bf h1:tdjIiV7L9za+ojwn8Eaevs1cod8y38/R9/bjQ1JSwgo= +github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918191218-559c9dda767c h1:Sq49DXoaUOpAF8lHsQFz2tFPwBI4o/wjPgNHBe11io0= +github.com/summa-tx/bitcoin-spv/golang v1.0.0 h1:zz3p7SQGOtMMqh5BwcUPUXsCACT3+E1Gjpxcqi80Yac= +github.com/summa-tx/bitcoin-spv/golang v1.0.0/go.mod h1:S0xvnR5vlpgFy2iaHEgcpVJYeJ9ohoMlE2yagtwoe9Y= +github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= +github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= +github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= +github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5 h1:u8i49c+BxloX3XQ55cvzFNXplizZP/q00i+IlttUjAU= +github.com/tendermint/crypto v0.0.0-20180820045704-3764759f34a5/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= +github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +github.com/tendermint/go-amino v0.15.0 h1:TC4e66P59W7ML9+bxio17CPKnxW3nKIRAYskntMAoRk= +github.com/tendermint/go-amino v0.15.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= +github.com/tendermint/iavl v0.12.1 h1:JDfyhM/Hhrumu1CL1Nxrypm8sNTPYqmeHo1IZLiJoXM= +github.com/tendermint/iavl v0.12.1/go.mod h1:EoKMMv++tDOL5qKKVnoIqtVPshRrEPeJ0WsgDOLAauM= +github.com/tendermint/iavl v0.12.4 h1:hd1woxUGISKkfUWBA4mmmTwOua6PQZTJM/F0FDrmMV8= +github.com/tendermint/iavl v0.12.4/go.mod h1:8LHakzt8/0G3/I8FUU0ReNx98S/EP6eyPJkAUvEXT/o= +github.com/tendermint/tendermint v0.31.5 h1:vTet8tCq3B9/J9Yo11dNZ8pOB7NtSy++bVSfkP4KzR4= +github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpslATMVqiGgWbZrlc= +github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= +github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXHwyrV1QY= +github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE= +github.com/tendermint/tendermint v0.32.3 h1:GEnWpGQ795h5oTFNbfBLsY0LW/CW2j6p6HtiYNfxsgg= +github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= +github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/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= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/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= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +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/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= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= diff --git a/golang/x/relay/handler.go b/golang/x/relay/handler.go new file mode 100644 index 00000000..628323fa --- /dev/null +++ b/golang/x/relay/handler.go @@ -0,0 +1,29 @@ +package relay + +import ( + "encoding/hex" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +// NewHandler returns a handler for "nameservice" type messages. +func NewHandler(keeper Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case types.MsgSetLink: + return handleMsgSetLink(ctx, keeper, msg) + default: + errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +// Handle a message to set name +func handleMsgSetLink(ctx sdk.Context, keeper Keeper, msg types.MsgSetLink) sdk.Result { + b, _ := hex.DecodeString(msg.Header) + keeper.SetLink(ctx, b) // If so, set the name to the value specified in the msg.//s + return sdk.Result{} // return +} diff --git a/golang/x/relay/keeper.go b/golang/x/relay/keeper.go new file mode 100644 index 00000000..2ee929db --- /dev/null +++ b/golang/x/relay/keeper.go @@ -0,0 +1,40 @@ +package relay + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" +) + +// Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine +type Keeper struct { + storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context + cdc *codec.Codec // The wire codec for binary encoding/decoding. +} + +// NewKeeper instantiates a new keeper +func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { + return Keeper{ + storeKey: storeKey, + cdc: cdc, + } +} + +func (k Keeper) setLink(ctx sdk.Context, header []byte) { + digest := btcspv.Hash256(header) + parent := btcspv.ExtractPrevBlockHashLE(header) + store := ctx.KVStore(k.storeKey) + store.Set(digest[:], parent[:]) +} + +// SetLink sets a header parent +func (k Keeper) SetLink(ctx sdk.Context, header []byte) { + // TODO: Remove this + k.setLink(ctx, header) +} + +// GetLink gets headers links +func (k Keeper) GetLink(ctx sdk.Context, digest [32]byte) []byte { + store := ctx.KVStore(k.storeKey) + return store.Get(digest[:]) +} diff --git a/golang/x/relay/types/codec.go b/golang/x/relay/types/codec.go new file mode 100644 index 00000000..aaca3541 --- /dev/null +++ b/golang/x/relay/types/codec.go @@ -0,0 +1,17 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +// ModuleCdc is the codec for the module +var ModuleCdc = codec.New() + +func init() { + RegisterCodec(ModuleCdc) +} + +// RegisterCodec registers concrete types on the Amino codec +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(MsgSetLink{}, "summa-cosmos-relay/SetLink", nil) +} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go new file mode 100644 index 00000000..fae0c0ca --- /dev/null +++ b/golang/x/relay/types/errors.go @@ -0,0 +1,130 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + // DefaultCodespace is the default code space + DefaultCodespace sdk.CodespaceType = ModuleName + + // 100-block -- shared errors + + // BadHeaderLength means Header array length not divisible by 80 + BadHeaderLength sdk.CodeType = 101 + + // InsufficientWork means a block did not have sufficient work + InsufficientWork sdk.CodeType = 102 + + // UnknownBlock is the error code for unknown blocks + UnknownBlock sdk.CodeType = 103 + + // BadHeight occurs when a proposed descendant is below a proposed ancestor + BadHeight sdk.CodeType = 104 + + // 200-block -- AddHeaders + + // RetargetOnExternal indicates a retarget was seen in AddHeaders + RetargetOnExternal sdk.CodeType = 201 + + // UnexptectedRetarget indicates a retarget was seen during AddHeaders loop + UnexptectedRetarget sdk.CodeType = 202 + + // BadLink indicates a broken link was found in the header array during AddHeaders loop + BadLink sdk.CodeType = 203 + + // 300-block AddHeadersWithRetarget + + // WrongEnd means the end block is at the wrong height + WrongEnd sdk.CodeType = 301 + + // WrongStart means the start block is at the wrong height + WrongStart sdk.CodeType = 302 + + // PeriodMismatch means the start and end block do not have the same difficulty + PeriodMismatch sdk.CodeType = 303 + + // BadRetarget means the provided blocks did not create the expected retarget + BadRetarget sdk.CodeType = 304 + + // 400-block -- MarkNewBestHeight + + // NotBestKnown means a block should have been the best known, but wasn't + NotBestKnown sdk.CodeType = 403 + + // NotHeaviestAncestor means a later common ancestor was found + NotHeaviestAncestor sdk.CodeType = 404 + + // NotHeavier means the proposed new best is not heavier than the current best + NotHeavier sdk.CodeType = 405 +) + +// ErrBadHeaderLength throws an error +func ErrBadHeaderLength(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadHeaderLength, "Header array length must be divisible by 80") +} + +// ErrInsufficientWork throws an error +func ErrInsufficientWork(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, InsufficientWork, "Header work is insufficient") +} + +// ErrUnknownBlock throws an error +func ErrUnknownBlock(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, UnknownBlock, "Unknown block") +} + +// ErrBadHeight throws an error +func ErrBadHeight(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadHeight, "A descendant height is below the ancestor height") +} + +// ErrRetargetOnExternal throws an error +func ErrRetargetOnExternal(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, RetargetOnExternal, "Unexpected retarget on external call") +} + +// ErrUnexptectedRetarget throws an error +func ErrUnexptectedRetarget(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, UnexptectedRetarget, "Target changed unexpectedly") +} + +// ErrBadLink throws an error +func ErrBadLink(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadLink, "Headers do not form a consistent chain") +} + +// ErrWrongEnd throws an error +func ErrWrongEnd(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, WrongEnd, "Must provide the last header of the closing difficulty period") +} + +// ErrWrongStart throws an error +func ErrWrongStart(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, WrongStart, "Must provide exactly 1 difficulty period") +} + +// ErrPeriodMismatch throws an error +func ErrPeriodMismatch(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, PeriodMismatch, "Period header difficulties do not match") +} + +// ErrBadRetarget throws an error +func ErrBadRetarget(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadRetarget, "Invalid retarget provided") +} + +// ErrNotBestKnown throws an error +func ErrNotBestKnown(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotBestKnown, "New best is unknown") +} + +// ErrNotHeaviestAncestor throws an error +func ErrNotHeaviestAncestor(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotHeaviestAncestor, "Ancestor must be heaviest common ancestor") +} + +// ErrNotHeavier throws an error +func ErrNotHeavier(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotHeavier, "New best hash does not have more work than previous") +} diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go new file mode 100644 index 00000000..4cbd83bc --- /dev/null +++ b/golang/x/relay/types/keys.go @@ -0,0 +1,9 @@ +package types + +const ( + // ModuleName is the name of the module + ModuleName = "summa-cosmos-relay" + + // StoreKey to be used when creating the KVStore + StoreKey = ModuleName +) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go new file mode 100644 index 00000000..42fd9cc9 --- /dev/null +++ b/golang/x/relay/types/msgs.go @@ -0,0 +1,47 @@ +package types + +import ( + "encoding/hex" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RouterKey is a name for the router +const RouterKey = ModuleName // this was defined in your key.go file + +// MsgSetLink defines a SetLink message +type MsgSetLink struct { + Header string `json:"header"` +} + +// NewMsgSetLink is a constructor function for MsgSetLink +func NewMsgSetLink(header string, owner sdk.AccAddress) MsgSetLink { + return MsgSetLink{ + Header: header, + } +} + +// Route should return the name of the module +func (msg MsgSetLink) Route() string { return RouterKey } + +// Type should return the action +func (msg MsgSetLink) Type() string { return "set_link" } + +// ValidateBasic runs stateless checks on the message +func (msg MsgSetLink) ValidateBasic() sdk.Error { + b, err := hex.DecodeString(msg.Header) + if err != nil || len(b) != 80 { + return ErrBadHeaderLength(DefaultCodespace) + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgSetLink) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgSetLink) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{} +} diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go new file mode 100644 index 00000000..ab1254f4 --- /dev/null +++ b/golang/x/relay/types/querier.go @@ -0,0 +1 @@ +package types From 21478695c3fb888a778ebb2648a192eff911fdf3 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sun, 22 Sep 2019 11:44:00 -0700 Subject: [PATCH 002/148] features: ongoing work on barebones sdk module --- golang/go.mod | 3 +- golang/go.sum | 17 +++++++++ golang/x/relay/alias.go | 37 +++++++++++++++++++ golang/x/relay/{ => keeper}/handler.go | 2 +- golang/x/relay/{ => keeper}/keeper.go | 2 +- golang/x/relay/keeper/querier.go | 51 ++++++++++++++++++++++++++ golang/x/relay/types/querier.go | 13 +++++++ 7 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 golang/x/relay/alias.go rename golang/x/relay/{ => keeper}/handler.go (98%) rename golang/x/relay/{ => keeper}/keeper.go (98%) create mode 100644 golang/x/relay/keeper/querier.go diff --git a/golang/go.mod b/golang/go.mod index 9a341406..2d9e8e65 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -5,7 +5,7 @@ go 1.12 require ( bou.ke/monkey v1.0.1 // indirect github.com/cosmos/cosmos-sdk v0.37.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect + github.com/cosmos/sdk-application-tutorial v0.37.0 github.com/gogo/protobuf v1.2.1 github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1 // indirect github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 // indirect @@ -13,4 +13,5 @@ require ( github.com/summa-tx/bitcoin-spv/golang v1.0.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 + google.golang.org/appengine v1.4.0 // indirect ) diff --git a/golang/go.sum b/golang/go.sum index 577f7413..43a93397 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -34,6 +34,8 @@ github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6 github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= +github.com/cosmos/sdk-application-tutorial v0.37.0 h1:KMk6qsTTEfvb0Y6O6lCdQC8OryNmnANfscJ4g7Qw3a0= +github.com/cosmos/sdk-application-tutorial v0.37.0/go.mod h1:RvG8/WZFCSo/MVhKCp4U0PPnpzlzcUx+lZfAdkjqObs= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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= @@ -93,6 +95,8 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -126,6 +130,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb h1:LvNCMEj0FFZQYsxZb7o3xQPrtqOOB6lrTUOWshC+ZTs= +github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.5 h1:Ly2UjURzxnsSYS0zI50fZ+srA+Fu7EbpV5hglvJvJG0= github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= @@ -138,6 +144,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -201,12 +209,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-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/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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= @@ -225,21 +237,26 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190329044733-9eb1bfa1ce65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d h1:XB2jc5XQ9uhizGTS2vWcN01bc4dI6z3C4KY5MQm8SS8= +google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go new file mode 100644 index 00000000..2acddb5d --- /dev/null +++ b/golang/x/relay/alias.go @@ -0,0 +1,37 @@ +package relay + +import ( + "github.com/summa-tx/relays/golang/x/relay/keeper" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +const ( + // ModuleName is what it says on the tin + ModuleName = types.ModuleName + // RouterKey is what it says on the tin + RouterKey = types.RouterKey + //StoreKey is what it says on the tin + StoreKey = types.StoreKey +) + +var ( + // NewKeeper is what is says on the tin + NewKeeper = keeper.NewKeeper + // NewQuerier is what is says on the tin + NewQuerier = keeper.NewQuerier + // NewMsgSetLink is what is says on the tin + NewMsgSetLink = types.NewMsgSetLink + // RegisterCodec is what is says on the tin + RegisterCodec = types.RegisterCodec + // ModuleCdc is what is says on the tin + ModuleCdc = types.ModuleCdc +) + +type ( + // Keeper is what is says on the tin + Keeper = keeper.Keeper + // MsgSetName is what is says on the tin + MsgSetName = types.MsgSetLink + // QueryResGetParent is what is says on the tin + QueryResGetParent = types.QueryResGetParent +) diff --git a/golang/x/relay/handler.go b/golang/x/relay/keeper/handler.go similarity index 98% rename from golang/x/relay/handler.go rename to golang/x/relay/keeper/handler.go index 628323fa..7e2c9b4a 100644 --- a/golang/x/relay/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -1,4 +1,4 @@ -package relay +package keeper import ( "encoding/hex" diff --git a/golang/x/relay/keeper.go b/golang/x/relay/keeper/keeper.go similarity index 98% rename from golang/x/relay/keeper.go rename to golang/x/relay/keeper/keeper.go index 2ee929db..b3cb0302 100644 --- a/golang/x/relay/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -1,4 +1,4 @@ -package relay +package keeper import ( "github.com/cosmos/cosmos-sdk/codec" diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go new file mode 100644 index 00000000..74249869 --- /dev/null +++ b/golang/x/relay/keeper/querier.go @@ -0,0 +1,51 @@ +package keeper + +import ( + "bytes" + "encoding/hex" + + "github.com/summa-tx/relays/golang/x/relay/types" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// QueryGetParent is a query string tag for get parent +const QueryGetParent = "getparent" + +// NewQuerier makes a query routing function +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case QueryGetParent: + return queryGetParent(ctx, path[1:], req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown nameservice query endpoint") + } + } +} + +func queryGetParent(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + digest, err := hex.DecodeString(path[0]) + if err != nil || len(digest) != 32 { + panic("could not unhex argument string") + } + + var buf [32]byte + copy(buf[:], digest[:32]) + parent := keeper.GetLink(ctx, buf) + + if bytes.Equal(parent, []byte{}) { + return []byte{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + + response := types.QueryResGetParent{ + Digest: hex.EncodeToString(digest), + Parent: hex.EncodeToString(parent)} + res, err := codec.MarshalJSONIndent(keeper.cdc, response) + if err != nil { + panic("could not marshal result to JSON") + } + return res, nil +} diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index ab1254f4..f548295b 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -1 +1,14 @@ package types + +import "strings" + +// QueryResGetParent is the payload for a GetParent Query +type QueryResGetParent struct { + Digest string `json:"digest"` + Parent string `json:"parent"` +} + +// implement fmt.Stringer +func (r QueryResGetParent) String() string { + return strings.Join([]string{r.Digest, r.Parent}, "\n") +} From 9bf6112ac9faf951212e271c51f1c3d137e4801c Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sun, 22 Sep 2019 15:44:52 -0700 Subject: [PATCH 003/148] feature: finish up basic module development --- golang/Makefile | 14 ++ golang/app.go | 265 ++++++++++++++++++++++++++-- golang/cmd/relaycli/main.go | 139 +++++++++++++++ golang/cmd/relayd/main.go | 83 +++++++++ golang/go.mod | 6 +- golang/go.sum | 1 + golang/x/relay/client/cli/query.go | 49 +++++ golang/x/relay/client/cli/tx.go | 52 ++++++ golang/x/relay/client/rest/query.go | 27 +++ golang/x/relay/client/rest/rest.go | 19 ++ golang/x/relay/client/rest/tx.go | 52 ++++++ golang/x/relay/genesis.go | 60 +++++++ golang/x/relay/module.go | 135 ++++++++++++++ golang/x/relay/types/keys.go | 2 +- golang/x/relay/types/types.go | 7 + 15 files changed, 898 insertions(+), 13 deletions(-) create mode 100644 golang/Makefile create mode 100644 golang/cmd/relaycli/main.go create mode 100644 golang/cmd/relayd/main.go create mode 100644 golang/x/relay/client/cli/query.go create mode 100644 golang/x/relay/client/cli/tx.go create mode 100644 golang/x/relay/client/rest/query.go create mode 100644 golang/x/relay/client/rest/rest.go create mode 100644 golang/x/relay/client/rest/tx.go create mode 100644 golang/x/relay/genesis.go create mode 100644 golang/x/relay/module.go create mode 100644 golang/x/relay/types/types.go diff --git a/golang/Makefile b/golang/Makefile new file mode 100644 index 00000000..e3ed2be3 --- /dev/null +++ b/golang/Makefile @@ -0,0 +1,14 @@ +all: lint install + +install: go.sum + go install -mod=readonly $(BUILD_FLAGS) ./cmd/relayd + go install -mod=readonly $(BUILD_FLAGS) ./cmd/relaycli + +go.sum: go.mod + @echo "--> Ensure dependencies have not been modified" + GO111MODULE=on go mod verify + +lint: + golangci-lint run + @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s + go mod verify diff --git a/golang/app.go b/golang/app.go index 81963627..58e1c90a 100644 --- a/golang/app.go +++ b/golang/app.go @@ -2,23 +2,28 @@ package relay import ( "os" + "encoding/json" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/genaccounts" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" "github.com/tendermint/tendermint/libs/log" - // "github.com/cosmos/cosmos-sdk/x/auth/genaccounts" - // - // "github.com/cosmos/cosmos-sdk/x/bank" - // distr "github.com/cosmos/cosmos-sdk/x/distribution" - // "github.com/cosmos/cosmos-sdk/x/params" - // "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/summa-tx/relays/golang/x/relay" + tmtypes "github.com/tendermint/tendermint/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + abci "github.com/tendermint/tendermint/abci/types" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - // abci "github.com/tendermint/tendermint/abci/types" - // cmn "github.com/tendermint/tendermint/libs/common" + cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tm-db" ) @@ -32,12 +37,50 @@ var ( DefaultNodeHome = os.ExpandEnv("$HOME/.summa/cosmosrelay/config") // ModuleBasics is basic module elemnets - ModuleBasics = module.NewBasicManager() + ModuleBasics = module.NewBasicManager( + genaccounts.AppModuleBasic{}, + genutil.AppModuleBasic{}, + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + distr.AppModuleBasic{}, + params.AppModuleBasic{}, + slashing.AppModuleBasic{}, + supply.AppModuleBasic{}, + + relay.AppModule{}, + ) + + // account permissions + maccPerms = map[string][]string{ + auth.FeeCollectorName: nil, + distr.ModuleName: nil, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + } ) type relayApp struct { *bam.BaseApp cdc *codec.Codec + + // keys to access the substores + keys map[string]*sdk.KVStoreKey + tkeys map[string]*sdk.TransientStoreKey + + // Keepers + accountKeeper auth.AccountKeeper + bankKeeper bank.Keeper + stakingKeeper staking.Keeper + slashingKeeper slashing.Keeper + distrKeeper distr.Keeper + supplyKeeper supply.Keeper + paramsKeeper params.Keeper + + relayKeeper relay.Keeper + + // Module Manager + mm *module.Manager } // MakeCodec generates the necessary codecs for Amino @@ -50,17 +93,217 @@ func MakeCodec() *codec.Codec { } // NewRelayApp instantiates a new Bitcoin relay app -func NewRelayApp(logger log.Logger, db dbm.DB) *relayApp { +func NewRelayApp( + logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp), +) *relayApp { // First define the top level codec that will be shared by the different modules. Note: Codec will be explained later cdc := MakeCodec() // BaseApp handles interactions with Tendermint through the ABCI protocol - bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc)) + bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) + keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, + supply.StoreKey, distr.StoreKey, slashing.StoreKey, params.StoreKey) + tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) + + // Here you initialize your application with the store keys it requires var app = &relayApp{ BaseApp: bApp, cdc: cdc, + keys: keys, + tkeys: tkeys, + } + + // The ParamsKeeper handles parameter storage for the application + app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) + // Set specific supspaces + authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace) + bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace) + stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace) + distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace) + slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace) + + // The AccountKeeper handles address -> account lookups + app.accountKeeper = auth.NewAccountKeeper( + app.cdc, + keys[auth.StoreKey], + authSubspace, + auth.ProtoBaseAccount, + ) + + // The BankKeeper allows you perform sdk.Coins interactions + app.bankKeeper = bank.NewBaseKeeper( + app.accountKeeper, + bankSubspace, + bank.DefaultCodespace, + app.ModuleAccountAddrs(), + ) + + // The SupplyKeeper collects transaction fees and renders them to the fee distribution module + app.supplyKeeper = supply.NewKeeper( + app.cdc, + keys[supply.StoreKey], + app.accountKeeper, + app.bankKeeper, + maccPerms, + ) + + // The staking keeper + stakingKeeper := staking.NewKeeper( + app.cdc, + keys[staking.StoreKey], + tkeys[staking.TStoreKey], + app.supplyKeeper, + stakingSubspace, + staking.DefaultCodespace, + ) + + app.distrKeeper = distr.NewKeeper( + app.cdc, + keys[distr.StoreKey], + distrSubspace, + &stakingKeeper, + app.supplyKeeper, + distr.DefaultCodespace, + auth.FeeCollectorName, + app.ModuleAccountAddrs(), + ) + + app.slashingKeeper = slashing.NewKeeper( + app.cdc, + keys[slashing.StoreKey], + &stakingKeeper, + slashingSubspace, + slashing.DefaultCodespace, + ) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.stakingKeeper = *stakingKeeper.SetHooks( + staking.NewMultiStakingHooks( + app.distrKeeper.Hooks(), + app.slashingKeeper.Hooks()), + ) + + // The NameserviceKeeper is the Keeper from the module for this tutorial + // It handles interactions with the namestore + app.relayKeeper = relay.NewKeeper( + keys[relay.StoreKey], + app.cdc, + ) + + app.mm = module.NewManager( + genaccounts.NewAppModule(app.accountKeeper), + genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx), + auth.NewAppModule(app.accountKeeper), + bank.NewAppModule(app.bankKeeper, app.accountKeeper), + relay.NewAppModule(app.relayKeeper, app.bankKeeper), + supply.NewAppModule(app.supplyKeeper, app.accountKeeper), + distr.NewAppModule(app.distrKeeper, app.supplyKeeper), + slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), + staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper), + ) + + app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) + app.mm.SetOrderEndBlockers(staking.ModuleName) + + // Sets the order of Genesis - Order matters, genutil is to always come last + app.mm.SetOrderInitGenesis( + genaccounts.ModuleName, + distr.ModuleName, + staking.ModuleName, + auth.ModuleName, + bank.ModuleName, + slashing.ModuleName, + relay.ModuleName, + genutil.ModuleName, + ) + + // register all module routes and module queriers + app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + + // The initChainer handles translating the genesis.json file into initial state for the network + app.SetInitChainer(app.InitChainer) + app.SetBeginBlocker(app.BeginBlocker) + app.SetEndBlocker(app.EndBlocker) + + // The AnteHandler handles signature verification and transaction pre-processing + app.SetAnteHandler( + auth.NewAnteHandler( + app.accountKeeper, + app.supplyKeeper, + auth.DefaultSigVerificationGasConsumer, + ), + ) + + // initialize stores + app.MountKVStores(keys) + app.MountTransientStores(tkeys) + + err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) + if err != nil { + cmn.Exit(err.Error()) } return app } + +// GenesisState represents chain state at the start of the chain. Any initial state (account balances) are stored here. +type GenesisState map[string]json.RawMessage + +// NewDefaultGenesisState returns a new default genesis +func NewDefaultGenesisState() GenesisState { + return ModuleBasics.DefaultGenesis() +} + +func (app *relayApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + + err := app.cdc.UnmarshalJSON(req.AppStateBytes, &genesisState) + if err != nil { + panic(err) + } + + return app.mm.InitGenesis(ctx, genesisState) +} + +func (app *relayApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { + return app.mm.BeginBlock(ctx, req) +} + +func (app *relayApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock { + return app.mm.EndBlock(ctx, req) +} + +func (app *relayApp) LoadHeight(height int64) error { + return app.LoadVersion(height, app.keys[bam.MainStoreKey]) +} + +// ModuleAccountAddrs returns all the app's module account addresses. +func (app *relayApp) ModuleAccountAddrs() map[string]bool { + modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[supply.NewModuleAddress(acc).String()] = true + } + + return modAccAddrs +} + +//_________________________________________________________ + +func (app *relayApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string, +) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { + + // as if they could withdraw from the start of the next block + ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) + + genState := app.mm.ExportGenesis(ctx) + appState, err = codec.MarshalJSONIndent(app.cdc, genState) + if err != nil { + return nil, nil, err + } + + validators = staking.WriteValidators(ctx, app.stakingKeeper) + + return appState, validators, nil +} diff --git a/golang/cmd/relaycli/main.go b/golang/cmd/relaycli/main.go new file mode 100644 index 00000000..86de917c --- /dev/null +++ b/golang/cmd/relaycli/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "os" + "path" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/lcd" + "github.com/cosmos/cosmos-sdk/client/rpc" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + "github.com/spf13/cobra" + "github.com/spf13/viper" + app "github.com/summa-tx/relays/golang" + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/cli" +) + +func main() { + cobra.EnableCommandSorting = false + + cdc := app.MakeCodec() + + // Read in the configuration file for the sdk + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + + rootCmd := &cobra.Command{ + Use: "nscli", + Short: "nameservice Client", + } + + // Add --chain-id to persistent flags and mark it required + rootCmd.PersistentFlags().String(client.FlagChainID, "", "Chain ID of tendermint node") + rootCmd.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { + return initConfig(rootCmd) + } + + // Construct Root Command + rootCmd.AddCommand( + rpc.StatusCommand(), + client.ConfigCmd(app.DefaultCLIHome), + queryCmd(cdc), + txCmd(cdc), + client.LineBreak, + lcd.ServeCommand(cdc, registerRoutes), + client.LineBreak, + keys.Commands(), + client.LineBreak, + version.Cmd, + client.NewCompletionCmd(rootCmd, true), + ) + + executor := cli.PrepareMainCmd(rootCmd, "NS", app.DefaultCLIHome) + err := executor.Execute() + if err != nil { + panic(err) + } +} + +func registerRoutes(rs *lcd.RestServer) { + client.RegisterRoutes(rs.CliCtx, rs.Mux) + app.ModuleBasics.RegisterRESTRoutes(rs.CliCtx, rs.Mux) +} + +func queryCmd(cdc *amino.Codec) *cobra.Command { + queryCmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + } + + queryCmd.AddCommand( + authcmd.GetAccountCmd(cdc), + client.LineBreak, + rpc.ValidatorCommand(cdc), + rpc.BlockCommand(), + authcmd.QueryTxsByEventsCmd(cdc), + authcmd.QueryTxCmd(cdc), + client.LineBreak, + ) + + // add modules' query commands + app.ModuleBasics.AddQueryCommands(queryCmd, cdc) + + return queryCmd +} + +func txCmd(cdc *amino.Codec) *cobra.Command { + txCmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + } + + txCmd.AddCommand( + bankcmd.SendTxCmd(cdc), + client.LineBreak, + authcmd.GetSignCommand(cdc), + authcmd.GetMultiSignCommand(cdc), + client.LineBreak, + authcmd.GetBroadcastCommand(cdc), + authcmd.GetEncodeCommand(cdc), + client.LineBreak, + ) + + // add modules' tx commands + app.ModuleBasics.AddTxCommands(txCmd, cdc) + + return txCmd +} + +func initConfig(cmd *cobra.Command) error { + home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) + if err != nil { + return err + } + + cfgFile := path.Join(home, "config", "config.toml") + if _, err := os.Stat(cfgFile); err == nil { + viper.SetConfigFile(cfgFile) + + if err := viper.ReadInConfig(); err != nil { + return err + } + } + if err := viper.BindPFlag(client.FlagChainID, cmd.PersistentFlags().Lookup(client.FlagChainID)); err != nil { + return err + } + if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { + return err + } + return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) +} diff --git a/golang/cmd/relayd/main.go b/golang/cmd/relayd/main.go new file mode 100644 index 00000000..4dc2011f --- /dev/null +++ b/golang/cmd/relayd/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "encoding/json" + "io" + + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/x/genaccounts" + genaccscli "github.com/cosmos/cosmos-sdk/x/genaccounts/client/cli" + "github.com/cosmos/cosmos-sdk/x/staking" + + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/libs/cli" + "github.com/tendermint/tendermint/libs/log" + + sdk "github.com/cosmos/cosmos-sdk/types" + genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" + abci "github.com/tendermint/tendermint/abci/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + app "github.com/summa-tx/relays/golang" +) + +func main() { + cobra.EnableCommandSorting = false + + cdc := app.MakeCodec() + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) + config.Seal() + + ctx := server.NewDefaultContext() + + rootCmd := &cobra.Command{ + Use: "relayd", + Short: "relay App Daemon (server)", + PersistentPreRunE: server.PersistentPreRunEFn(ctx), + } + // CLI commands to initialize the chain + rootCmd.AddCommand( + genutilcli.InitCmd(ctx, cdc, app.ModuleBasics, app.DefaultNodeHome), + genutilcli.CollectGenTxsCmd(ctx, cdc, genaccounts.AppModuleBasic{}, app.DefaultNodeHome), + genutilcli.GenTxCmd(ctx, cdc, app.ModuleBasics, staking.AppModuleBasic{}, genaccounts.AppModuleBasic{}, app.DefaultNodeHome, app.DefaultCLIHome), + genutilcli.ValidateGenesisCmd(ctx, cdc, app.ModuleBasics), + // AddGenesisAccountCmd allows users to add accounts to the genesis file + genaccscli.AddGenesisAccountCmd(ctx, cdc, app.DefaultNodeHome, app.DefaultCLIHome), + ) + + server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) + + // prepare and add flags + executor := cli.PrepareBaseCmd(rootCmd, "NS", app.DefaultNodeHome) + err := executor.Execute() + if err != nil { + panic(err) + } +} + +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { + return app.NewRelayApp(logger, db) +} + +func exportAppStateAndTMValidators( + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, +) (json.RawMessage, []tmtypes.GenesisValidator, error) { + + if height != -1 { + relayApp := app.NewRelayApp(logger, db) + err := relayApp.LoadHeight(height) + if err != nil { + return nil, nil, err + } + return relayApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) + } + + relayApp := app.NewRelayApp(logger, db) + + return relayApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) +} diff --git a/golang/go.mod b/golang/go.mod index 2d9e8e65..1bf0d722 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -5,12 +5,16 @@ go 1.12 require ( bou.ke/monkey v1.0.1 // indirect github.com/cosmos/cosmos-sdk v0.37.0 - github.com/cosmos/sdk-application-tutorial v0.37.0 + github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gogo/protobuf v1.2.1 + github.com/gorilla/mux v1.7.0 github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1 // indirect github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 // indirect github.com/otiai10/mint v1.2.3 // indirect + github.com/spf13/cobra v0.0.5 + github.com/spf13/viper v1.3.2 github.com/summa-tx/bitcoin-spv/golang v1.0.0 + github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 google.golang.org/appengine v1.4.0 // indirect diff --git a/golang/go.sum b/golang/go.sum index 43a93397..05d6da62 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -173,6 +173,7 @@ github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918190112-a7c5660022bf h1:tdj github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918191218-559c9dda767c h1:Sq49DXoaUOpAF8lHsQFz2tFPwBI4o/wjPgNHBe11io0= github.com/summa-tx/bitcoin-spv/golang v1.0.0 h1:zz3p7SQGOtMMqh5BwcUPUXsCACT3+E1Gjpxcqi80Yac= github.com/summa-tx/bitcoin-spv/golang v1.0.0/go.mod h1:S0xvnR5vlpgFy2iaHEgcpVJYeJ9ohoMlE2yagtwoe9Y= +github.com/summa-tx/relays v0.0.0-20190918181406-3f7ee83f6828 h1:QusoX/pzugVlaLfDytFABL1iOU5hqata2o4u1BBHyRA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go new file mode 100644 index 00000000..5fa22f55 --- /dev/null +++ b/golang/x/relay/client/cli/query.go @@ -0,0 +1,49 @@ +package cli + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/spf13/cobra" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +// GetQueryCmd sets up query CLI commands +func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + relayQueryCommand := &cobra.Command{ + Use: types.ModuleName, + Short: "Querying commands for the relay module", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + relayQueryCommand.AddCommand(client.GetCommands( + GetCommandGetParent(storeKey, cdc), + )...) + return relayQueryCommand +} + +// GetCommandGetParent queries information about a name +func GetCommandGetParent(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "getparent [digest]", + Short: "getparent digest", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + digest := args[0] + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/getparent/%s", queryRoute, digest), nil) + if err != nil { + fmt.Printf("could not get parent - %s \n", digest) + return nil + } + + var out types.QueryResGetParent + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(out) + }, + } +} diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go new file mode 100644 index 00000000..374e49cf --- /dev/null +++ b/golang/x/relay/client/cli/tx.go @@ -0,0 +1,52 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +// GetTxCmd sets up transaction CLI commands +func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { + relayTxCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Relay transaction subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + relayTxCmd.AddCommand(client.PostCommands( + GetCmdSetLink(cdc), + )...) + + return relayTxCmd +} + +// GetCmdSetLink is the CLI command for sending a SetLink transaction +func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "setlink [name]", + Short: "Set a link", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + msg := types.NewMsgSetLink(args[0], cliCtx.GetFromAddress()) + err := msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, + } +} diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go new file mode 100644 index 00000000..1185f908 --- /dev/null +++ b/golang/x/relay/client/rest/query.go @@ -0,0 +1,27 @@ +package rest + +import ( + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/cosmos/cosmos-sdk/types/rest" + + "github.com/gorilla/mux" +) + +func getParentHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + paramType := vars[restName] + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/resolve/%s", storeName, paramType), nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go new file mode 100644 index 00000000..a6f42314 --- /dev/null +++ b/golang/x/relay/client/rest/rest.go @@ -0,0 +1,19 @@ +package rest + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/context" + + "github.com/gorilla/mux" +) + +const ( + restName = "relay" +) + +// RegisterRoutes - Central function to define routes that get registered by the main application +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { + r.HandleFunc(fmt.Sprintf("/%s/link", storeName), setLinkHandler(cliCtx)).Methods("POST") + r.HandleFunc(fmt.Sprintf("/%s/parent/{%s}", storeName, restName), getParentHandler(cliCtx, storeName)).Methods("GET") +} diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go new file mode 100644 index 00000000..343e2b8b --- /dev/null +++ b/golang/x/relay/client/rest/tx.go @@ -0,0 +1,52 @@ +package rest + +import ( + "net/http" + + "github.com/summa-tx/relays/golang/x/relay/types" + + "github.com/cosmos/cosmos-sdk/client/context" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" +) + +type setLinkReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Header string `json:"header"` + Sender string `json:"sender"` +} + +func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req setLinkReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // create the message + msg := types.NewMsgSetLink(req.Header, addr) + err = msg.ValidateBasic() + + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go new file mode 100644 index 00000000..b6e504c6 --- /dev/null +++ b/golang/x/relay/genesis.go @@ -0,0 +1,60 @@ +package relay + +import ( + "encoding/hex" + "fmt" + + "github.com/summa-tx/relays/golang/x/relay/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// GenesisState is the genesis state +type GenesisState struct { + Headers []string `json:"headers"` + Links []types.Link `json:"links"` +} + +// NewGenesisState instantiates a genesis state +func NewGenesisState(headers []string) GenesisState { + return GenesisState{Headers: headers, Links: []types.Link{}} +} + +// ValidateGenesis Validates a genesis state +func ValidateGenesis(data GenesisState) error { + for _, header := range data.Headers { + if len(header) != 160 { + return fmt.Errorf("Invalid header, bad length") + } + + _, err := hex.DecodeString(header) + if err != nil { + return err + } + } + return nil +} + +// DefaultGenesisState makes a default empty genesis state +func DefaultGenesisState() GenesisState { + return GenesisState{ + Headers: []string{}, + Links: []types.Link{}, + } +} + +// InitGenesis inits the app state based on the genesis state +func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate { + for _, header := range data.Headers { + // can't fail if passes ValidateGenesis + h, _ := hex.DecodeString(header) + keeper.SetLink(ctx, h) + } + return []abci.ValidatorUpdate{} +} + +// ExportGenesis exports the genesis state +func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { + panic("Not implemented") +} diff --git a/golang/x/relay/module.go b/golang/x/relay/module.go new file mode 100644 index 00000000..3d6826c3 --- /dev/null +++ b/golang/x/relay/module.go @@ -0,0 +1,135 @@ +package relay + +import ( + "encoding/json" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/bank" + + "github.com/summa-tx/relays/golang/x/relay/client/cli" + "github.com/summa-tx/relays/golang/x/relay/client/rest" + "github.com/summa-tx/relays/golang/x/relay/keeper" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// type check to ensure the interface is properly implemented +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} +) + +// AppModuleBasic is app module Basics object +type AppModuleBasic struct{} + +// Name is +func (AppModuleBasic) Name() string { + return ModuleName +} + +// RegisterCodec is +func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { + RegisterCodec(cdc) +} + +// DefaultGenesis is +func (AppModuleBasic) DefaultGenesis() json.RawMessage { + return ModuleCdc.MustMarshalJSON(DefaultGenesisState()) +} + +// ValidateGenesis validates check of the Genesis +func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { + var data GenesisState + err := ModuleCdc.UnmarshalJSON(bz, &data) + if err != nil { + return err + } + // Once json successfully marshalled, passes along to genesis.go + return ValidateGenesis(data) +} + +// RegisterRESTRoutes registers rest routes +func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { + rest.RegisterRoutes(ctx, rtr, StoreKey) +} + +// GetQueryCmd get the root query command of this module +func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetQueryCmd(StoreKey, cdc) +} + +// GetTxCmd get the root tx command of this module +func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { + return cli.GetTxCmd(StoreKey, cdc) +} + +// AppModule is the AppModule +type AppModule struct { + AppModuleBasic + keeper Keeper + coinKeeper bank.Keeper +} + +// NewAppModule creates a new AppModule Object +func NewAppModule(k Keeper, bankKeeper bank.Keeper) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: k, + coinKeeper: bankKeeper, + } +} + +// Name is +func (AppModule) Name() string { + return ModuleName +} + +// RegisterInvariants is +func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} + +// Route is +func (am AppModule) Route() string { + return RouterKey +} + +// NewHandler makes a new handler +func (am AppModule) NewHandler() sdk.Handler { + return keeper.NewHandler(am.keeper) +} + +// QuerierRoute is +func (am AppModule) QuerierRoute() string { + return ModuleName +} + +// NewQuerierHandler is +func (am AppModule) NewQuerierHandler() sdk.Querier { + return NewQuerier(am.keeper) +} + +// BeginBlock is +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} + +// EndBlock is +func (am AppModule) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// InitGenesis is +func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState GenesisState + ModuleCdc.MustUnmarshalJSON(data, &genesisState) + return InitGenesis(ctx, am.keeper, genesisState) +} + +// ExportGenesis is +func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper) + return ModuleCdc.MustMarshalJSON(gs) +} diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index 4cbd83bc..e88f27c6 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -2,7 +2,7 @@ package types const ( // ModuleName is the name of the module - ModuleName = "summa-cosmos-relay" + ModuleName = "relay" // StoreKey to be used when creating the KVStore StoreKey = ModuleName diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go new file mode 100644 index 00000000..a7b4d871 --- /dev/null +++ b/golang/x/relay/types/types.go @@ -0,0 +1,7 @@ +package types + +// Link is a link in the chain +type Link struct { + Digest string `json:"digest"` + Parent string `json:"parent"` +} From 0b0b7a6951da23ff7fe5df77f8ab75ea862c8883 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sun, 22 Sep 2019 15:46:45 -0700 Subject: [PATCH 004/148] chore: consistent naming --- golang/app.go | 2 +- golang/x/relay/types/codec.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/golang/app.go b/golang/app.go index 58e1c90a..ada4c3a0 100644 --- a/golang/app.go +++ b/golang/app.go @@ -27,7 +27,7 @@ import ( dbm "github.com/tendermint/tm-db" ) -const appName = "summa-cosmos-relay" +const appName = "relay" var ( // DefaultCLIHome is default home directories for the application CLI diff --git a/golang/x/relay/types/codec.go b/golang/x/relay/types/codec.go index aaca3541..dd2743c0 100644 --- a/golang/x/relay/types/codec.go +++ b/golang/x/relay/types/codec.go @@ -13,5 +13,5 @@ func init() { // RegisterCodec registers concrete types on the Amino codec func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgSetLink{}, "summa-cosmos-relay/SetLink", nil) + cdc.RegisterConcrete(MsgSetLink{}, "relay/SetLink", nil) } From 2a721d9dd795e5a5ef92f8fa27c2692ad0ec1d35 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sun, 22 Sep 2019 19:35:23 -0700 Subject: [PATCH 005/148] feature: SetLink and GetParent work now --- golang/app.go | 11 +++++++++-- golang/x/relay/client/cli/tx.go | 6 +++--- golang/x/relay/client/rest/tx.go | 2 +- golang/x/relay/keeper/keeper.go | 10 ++++++++++ golang/x/relay/types/msgs.go | 24 +++++++++++++++--------- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/golang/app.go b/golang/app.go index ada4c3a0..df241ff4 100644 --- a/golang/app.go +++ b/golang/app.go @@ -102,8 +102,15 @@ func NewRelayApp( // BaseApp handles interactions with Tendermint through the ABCI protocol bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) - keys := sdk.NewKVStoreKeys(bam.MainStoreKey, auth.StoreKey, staking.StoreKey, - supply.StoreKey, distr.StoreKey, slashing.StoreKey, params.StoreKey) + keys := sdk.NewKVStoreKeys( + bam.MainStoreKey, + auth.StoreKey, + staking.StoreKey, + supply.StoreKey, + distr.StoreKey, + slashing.StoreKey, + params.StoreKey, + relay.StoreKey) tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) // Here you initialize your application with the store keys it requires diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 374e49cf..ea09b398 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -32,15 +32,15 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { // GetCmdSetLink is the CLI command for sending a SetLink transaction func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "setlink [name]", + Use: "setlink [hex header] [signer address]", Short: "Set a link", - Args: cobra.ExactArgs(1), + Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - msg := types.NewMsgSetLink(args[0], cliCtx.GetFromAddress()) + msg := types.NewMsgSetLink(args[1], args[0], cliCtx.GetFromAddress()) err := msg.ValidateBasic() if err != nil { return err diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 343e2b8b..b8e41081 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -39,7 +39,7 @@ func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { } // create the message - msg := types.NewMsgSetLink(req.Header, addr) + msg := types.NewMsgSetLink(req.Sender, req.Header, addr) err = msg.ValidateBasic() if err != nil { diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index b3cb0302..d7160f52 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -1,6 +1,8 @@ package keeper import ( + "encoding/hex" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/bitcoin-spv/golang/btcspv" @@ -21,9 +23,14 @@ func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { } func (k Keeper) setLink(ctx sdk.Context, header []byte) { + println(hex.EncodeToString(header)) digest := btcspv.Hash256(header) + println(hex.EncodeToString(digest)) parent := btcspv.ExtractPrevBlockHashLE(header) + println(hex.EncodeToString(parent)) + println(k.storeKey) store := ctx.KVStore(k.storeKey) + println("store got") store.Set(digest[:], parent[:]) } @@ -35,6 +42,9 @@ func (k Keeper) SetLink(ctx sdk.Context, header []byte) { // GetLink gets headers links func (k Keeper) GetLink(ctx sdk.Context, digest [32]byte) []byte { + println(hex.EncodeToString(digest[:])) + println(k.storeKey) store := ctx.KVStore(k.storeKey) + println(1) return store.Get(digest[:]) } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 42fd9cc9..10861696 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -11,18 +11,26 @@ const RouterKey = ModuleName // this was defined in your key.go file // MsgSetLink defines a SetLink message type MsgSetLink struct { - Header string `json:"header"` + Address string `json:"signer"` + Header string `json:"header"` } // NewMsgSetLink is a constructor function for MsgSetLink -func NewMsgSetLink(header string, owner sdk.AccAddress) MsgSetLink { +func NewMsgSetLink(address, header string, owner sdk.AccAddress) MsgSetLink { return MsgSetLink{ - Header: header, + address, + header, } } -// Route should return the name of the module -func (msg MsgSetLink) Route() string { return RouterKey } +// GetSigners defines whose signature is required +func (msg MsgSetLink) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(msg.Address) + if err != nil { + return []sdk.AccAddress{} + } + return []sdk.AccAddress{addr} +} // Type should return the action func (msg MsgSetLink) Type() string { return "set_link" } @@ -41,7 +49,5 @@ func (msg MsgSetLink) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } -// GetSigners defines whose signature is required -func (msg MsgSetLink) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{} -} +// Route should return the name of the module +func (msg MsgSetLink) Route() string { return RouterKey } From caa37edbecf90e7a7c148873c0fb026982b0d31d Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sun, 22 Sep 2019 19:59:43 -0700 Subject: [PATCH 006/148] chore: README with notes on adding txns and queries --- golang/README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 golang/README.md diff --git a/golang/README.md b/golang/README.md new file mode 100644 index 00000000..1f687e38 --- /dev/null +++ b/golang/README.md @@ -0,0 +1,42 @@ +## TODOs: + +1. Migrate solidity logic +1. Add reasonable genesis state + +## How to add a view function +1. Add necessary getter(s) in `x/relay/keeper/keeper.go` +1. Add response type to `x/relay/types/querier.go` + 1. Response type is a struct with the return values + 1. Implement `String()` for the response type +1. Add function to querier `x/relay/keeper/querier.go` + 1. Add new string tag for the new query + 1. Add new `query___` function + 1. Add new case block to `switch` in `NewQuerier()` +1. Add alias type in `x/relay/alias.go` +1. Add to CLI + 1. add to `x/relay/client/cli/query.go` +1. Add to REST + 1. add to `x/relay/client/rest/query.go` + 1. add GET route to `x/relay/client/rest/rest.go` + +## How to add a non-view function +1. Add necessary getters/setters in `x/relay/keeper/keeper.go` +1. Add msg type in `x/relay/types/msgs.go` + 1. Message type is a struct with the arguments + 1. Implement `New___()` + 1. Implement `GetSigners()` + 1. Implement `Type()` + 1. Implement `ValidateBasic()` + 1. Implement `GetSignBytes()` + 1. Implement `Route()` +1. Add to handler + 1. Add new `handle____` function + 1. Add new case block to `switch` in `NewHandler()` +1. Add aliases in `x/relay/alias.go` + 1. Add alisa in `var` block + 1. Add alias in `type` block +1. Add to CLI + 1. add to `x/relay/client/cli/tx.go` +1. Add to REST + 1. add to `x/relay/client/rest/tx.go` + 1. add POST route to `x/relay/client/rest/rest.go` From 4a714d7a8575833b85f052fc588585ff08385ddb Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 23 Sep 2019 11:00:22 -0700 Subject: [PATCH 007/148] chore: add TODOs to README --- golang/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/golang/README.md b/golang/README.md index 1f687e38..bb7833cf 100644 --- a/golang/README.md +++ b/golang/README.md @@ -1,7 +1,9 @@ ## TODOs: 1. Migrate solidity logic +1. Add iterator for links 1. Add reasonable genesis state +1. Add state export ## How to add a view function 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` From 8bee3495d43e3f0d3833ae99441974f381dd48ca Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Mon, 23 Sep 2019 13:14:26 -0600 Subject: [PATCH 008/148] adding getters and setters --- golang/x/relay/keeper/keeper.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index d7160f52..174c5caa 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -48,3 +48,8 @@ func (k Keeper) GetLink(ctx sdk.Context, digest [32]byte) []byte { println(1) return store.Get(digest[:]) } + +// func (k Keeper) SetRelayGenesis(ctx sdk.Context, digest [32]byte) []byte { +// store := ctx.KVStore(k.storeKey) +// store.Set([32]byte()) +// } From 16165422f7337aa0be21d9b19d112d9535dea78e Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Mon, 23 Sep 2019 14:48:24 -0600 Subject: [PATCH 009/148] added getters and setters, getting errors --- golang/x/relay/keeper/keeper.go | 53 ++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 174c5caa..96dc6c2e 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -49,7 +49,52 @@ func (k Keeper) GetLink(ctx sdk.Context, digest [32]byte) []byte { return store.Get(digest[:]) } -// func (k Keeper) SetRelayGenesis(ctx sdk.Context, digest [32]byte) []byte { -// store := ctx.KVStore(k.storeKey) -// store.Set([32]byte()) -// } +func (k Keeper) GetRelayGenesis(ctx sdk.Context, digest [32]byte) [32]byte { + return k.GetLink(ctx, digest).RelayGenesis +} + +func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis [32]byte, digest [32]byte) { + link := k.GetLink(ctx, digest) + link.RelayGenesis = relayGenesis + k.SetLink(ctx, relayGenesis) +} + +func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest [32]byte) [32]byte { + return k.GetLink(ctx, digest).BestKnownDigest +} + +func (k Keeper) SetBestKnownDigest(ctx sdk.Context, bestKnownDigest [32]byte, digest [32]byte) { + link := k.GetLink(ctx, digest) + link.BestKnownDigest = bestKnownDigest + k.SetLink(ctx, bestKnownDigest) +} + +func (k Keeper) GetLastReorgCommonAncestor(ctx sdk.Context, digest [32]byte) [32]byte { + return k.GetLink(ctx, digest).LastReorgCommonAncestor +} + +func (k Keeper) SetLastReorgCommonAncestor(ctx sdk.Context, lastReorgCommonAncestor [32]byte, digest [32]byte) { + link := k.GetLink(ctx, digest) + link.LastReorgCommonAncestor = lastReorgCommonAncestor + k.SetLink(ctx, lastReorgCommonAncestor) +} + +func (k Keeper) GetPreviousBlock(ctx sdk.Context, digest [32]byte) [32]byte { + return k.GetLink(ctx, digest).PreviousBlock +} + +func (k Keeper) SetPreviousBlock(ctx sdk.Context, previousBlock [32]byte, digest [32]byte) { + link := k.GetLink(ctx, digest) + link.PreviousBlock = previousBlock + k.SetLink(ctx, previousBlock) +} + +func (k Keeper) GetBlockHeight(ctx sdk.Context, digest [32]byte) [32]byte { + return k.GetLink(ctx, digest).BlockHeight +} + +func (k Keeper) SetBlockHeight(ctx sdk.Context, blockHeight [32]byte, digest [32]byte) { + link := k.GetLink(ctx, digest) + link.BlockHeight = blockHeight + k.SetLink(ctx, blockHeight) +} From a4453876b62dd12721b0c6ac238b9599fb199db6 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 2 Oct 2019 13:38:41 -0700 Subject: [PATCH 010/148] feature: rewrite some getters and setters --- golang/x/relay/alias.go | 4 ++ golang/x/relay/keeper/keeper.go | 114 +++++++++++++++++-------------- golang/x/relay/keeper/querier.go | 4 +- golang/x/relay/types/keys.go | 6 ++ golang/x/relay/types/types.go | 3 + 5 files changed, 76 insertions(+), 55 deletions(-) diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index 2acddb5d..c5b4514b 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -30,6 +30,10 @@ var ( type ( // Keeper is what is says on the tin Keeper = keeper.Keeper + + // Hash256Digest 32-byte double-sha2 digest + Hash256Digest = types.Hash256Digest + // MsgSetName is what is says on the tin MsgSetName = types.MsgSetLink // QueryResGetParent is what is says on the tin diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 96dc6c2e..875c3435 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -1,7 +1,7 @@ package keeper import ( - "encoding/hex" + "github.com/summa-tx/relays/golang/x/relay/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -22,79 +22,87 @@ func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { } } +func digestFromSlice(buf []byte) types.Hash256Digest { + var digest types.Hash256Digest + copy(digest[:], buf) + return digest +} + func (k Keeper) setLink(ctx sdk.Context, header []byte) { - println(hex.EncodeToString(header)) digest := btcspv.Hash256(header) - println(hex.EncodeToString(digest)) parent := btcspv.ExtractPrevBlockHashLE(header) - println(hex.EncodeToString(parent)) - println(k.storeKey) store := ctx.KVStore(k.storeKey) - println("store got") store.Set(digest[:], parent[:]) } // SetLink sets a header parent func (k Keeper) SetLink(ctx sdk.Context, header []byte) { - // TODO: Remove this + // TODO: Remove this in favor of fully validating add headers k.setLink(ctx, header) } // GetLink gets headers links -func (k Keeper) GetLink(ctx sdk.Context, digest [32]byte) []byte { - println(hex.EncodeToString(digest[:])) - println(k.storeKey) +func (k Keeper) GetLink(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { store := ctx.KVStore(k.storeKey) - println(1) - return store.Get(digest[:]) -} - -func (k Keeper) GetRelayGenesis(ctx sdk.Context, digest [32]byte) [32]byte { - return k.GetLink(ctx, digest).RelayGenesis -} - -func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis [32]byte, digest [32]byte) { - link := k.GetLink(ctx, digest) - link.RelayGenesis = relayGenesis - k.SetLink(ctx, relayGenesis) -} - -func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest [32]byte) [32]byte { - return k.GetLink(ctx, digest).BestKnownDigest + result := store.Get(digest[:]) + return digestFromSlice(result) } -func (k Keeper) SetBestKnownDigest(ctx sdk.Context, bestKnownDigest [32]byte, digest [32]byte) { - link := k.GetLink(ctx, digest) - link.BestKnownDigest = bestKnownDigest - k.SetLink(ctx, bestKnownDigest) -} - -func (k Keeper) GetLastReorgCommonAncestor(ctx sdk.Context, digest [32]byte) [32]byte { - return k.GetLink(ctx, digest).LastReorgCommonAncestor -} +// GetRelayGenesis returns the first digest in the relay +func (k Keeper) GetRelayGenesis(ctx sdk.Context) types.Hash256Digest { + store := ctx.KVStore(k.storeKey) + result := store.Get([]byte(types.RelayGenesisStorage)) -func (k Keeper) SetLastReorgCommonAncestor(ctx sdk.Context, lastReorgCommonAncestor [32]byte, digest [32]byte) { - link := k.GetLink(ctx, digest) - link.LastReorgCommonAncestor = lastReorgCommonAncestor - k.SetLink(ctx, lastReorgCommonAncestor) + return digestFromSlice(result) } -func (k Keeper) GetPreviousBlock(ctx sdk.Context, digest [32]byte) [32]byte { - return k.GetLink(ctx, digest).PreviousBlock +// SetRelayGenesis sets the first digest in the relay +func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := ctx.KVStore(k.storeKey) + store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) } -func (k Keeper) SetPreviousBlock(ctx sdk.Context, previousBlock [32]byte, digest [32]byte) { - link := k.GetLink(ctx, digest) - link.PreviousBlock = previousBlock - k.SetLink(ctx, previousBlock) -} +// GetBestKnownDigest returns the best known digest in the relay +func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { + store := ctx.KVStore(k.storeKey) + result := store.Get([]byte(types.BestKnownDigestStorage)) -func (k Keeper) GetBlockHeight(ctx sdk.Context, digest [32]byte) [32]byte { - return k.GetLink(ctx, digest).BlockHeight + return digestFromSlice(result) } -func (k Keeper) SetBlockHeight(ctx sdk.Context, blockHeight [32]byte, digest [32]byte) { - link := k.GetLink(ctx, digest) - link.BlockHeight = blockHeight - k.SetLink(ctx, blockHeight) -} +// func (k Keeper) SetBestKnownDigest(ctx sdk.Context, bestKnownDigest types.Hash256Digest, digest types.Hash256Digest) { +// link := k.GetLink(ctx, digest) +// link.BestKnownDigest = bestKnownDigest +// k.SetLink(ctx, bestKnownDigest) +// } +// +// func (k Keeper) GetLastReorgCommonAncestor(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { +// return k.GetLink(ctx, digest).LastReorgCommonAncestor +// } +// +// func (k Keeper) SetLastReorgCommonAncestor(ctx sdk.Context, lastReorgCommonAncestor types.Hash256Digest, digest types.Hash256Digest) { +// link := k.GetLink(ctx, digest) +// link.LastReorgCommonAncestor = lastReorgCommonAncestor +// k.SetLink(ctx, lastReorgCommonAncestor) +// } +// +// func (k Keeper) GetPreviousBlock(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { +// return k.GetLink(ctx, digest).PreviousBlock +// } +// +// func (k Keeper) SetPreviousBlock(ctx sdk.Context, previousBlock types.Hash256Digest, digest types.Hash256Digest) { +// link := k.GetLink(ctx, digest) +// link.PreviousBlock = previousBlock +// k.SetLink(ctx, previousBlock) +// } +// +// func (k Keeper) GetBlockHeight(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { +// return k.GetLink(ctx, digest).BlockHeight +// } +// +// func (k Keeper) SetBlockHeight(ctx sdk.Context, blockHeight types.Hash256Digest, digest types.Hash256Digest) { +// link := k.GetLink(ctx, digest) +// link.BlockHeight = blockHeight +// k.SetLink(ctx, blockHeight) +// } diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index 74249869..ddd8b45b 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -36,13 +36,13 @@ func queryGetParent(ctx sdk.Context, path []string, req abci.RequestQuery, keepe copy(buf[:], digest[:32]) parent := keeper.GetLink(ctx, buf) - if bytes.Equal(parent, []byte{}) { + if bytes.Equal(parent[:], []byte{}) { return []byte{}, types.ErrUnknownBlock(types.DefaultCodespace) } response := types.QueryResGetParent{ Digest: hex.EncodeToString(digest), - Parent: hex.EncodeToString(parent)} + Parent: hex.EncodeToString(parent[:])} res, err := codec.MarshalJSONIndent(keeper.cdc, response) if err != nil { panic("could not marshal result to JSON") diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index e88f27c6..b236410c 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -6,4 +6,10 @@ const ( // StoreKey to be used when creating the KVStore StoreKey = ModuleName + + // RelayGenesisStorage is the storage key for the relay genesis digest + RelayGenesisStorage = "RelayGenesis" + + // BestKnownDigestStorage is the storage key for the best known digest + BestKnownDigestStorage = "BestKnownDigest" ) diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index a7b4d871..82f246ae 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -1,5 +1,8 @@ package types +// Hash256Digest 32-byte double-sha2 digest +type Hash256Digest = [32]byte + // Link is a link in the chain type Link struct { Digest string `json:"digest"` From 9cbf74908aa6594b4dfa85983cf54db284474417 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 11 Nov 2019 14:32:05 -0800 Subject: [PATCH 011/148] bug: correct alias of MsgSetLink --- golang/README.md | 2 ++ golang/go.mod | 5 ----- golang/x/relay/alias.go | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/golang/README.md b/golang/README.md index 1f687e38..6c01b673 100644 --- a/golang/README.md +++ b/golang/README.md @@ -2,6 +2,8 @@ 1. Migrate solidity logic 1. Add reasonable genesis state +1. Add state export +1. Consider storing more state than solidity does ## How to add a view function 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` diff --git a/golang/go.mod b/golang/go.mod index 1bf0d722..eb61d81b 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -3,14 +3,9 @@ module github.com/summa-tx/relays/golang go 1.12 require ( - bou.ke/monkey v1.0.1 // indirect github.com/cosmos/cosmos-sdk v0.37.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gogo/protobuf v1.2.1 github.com/gorilla/mux v1.7.0 - github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1 // indirect - github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95 // indirect - github.com/otiai10/mint v1.2.3 // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 github.com/summa-tx/bitcoin-spv/golang v1.0.0 diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index c5b4514b..ba1ab28a 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -34,8 +34,9 @@ type ( // Hash256Digest 32-byte double-sha2 digest Hash256Digest = types.Hash256Digest - // MsgSetName is what is says on the tin - MsgSetName = types.MsgSetLink + // MsgSetLink is what is says on the tin + MsgSetLink = types.MsgSetLink + // QueryResGetParent is what is says on the tin QueryResGetParent = types.QueryResGetParent ) From de20ac620e0c44bae3f2a7a734fb6b2c845bec3d Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 11 Nov 2019 14:56:57 -0800 Subject: [PATCH 012/148] refactor: improve Hash256 handling in keeper and querier --- golang/x/relay/keeper/keeper.go | 30 ++++++++++++++++++------------ golang/x/relay/keeper/querier.go | 7 +++++-- golang/x/relay/types/errors.go | 16 ++++++++++++++++ golang/x/relay/types/types.go | 14 +++++++++++++- 4 files changed, 52 insertions(+), 15 deletions(-) diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 875c3435..a4a14688 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -22,12 +22,6 @@ func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { } } -func digestFromSlice(buf []byte) types.Hash256Digest { - var digest types.Hash256Digest - copy(digest[:], buf) - return digest -} - func (k Keeper) setLink(ctx sdk.Context, header []byte) { digest := btcspv.Hash256(header) parent := btcspv.ExtractPrevBlockHashLE(header) @@ -42,18 +36,26 @@ func (k Keeper) SetLink(ctx sdk.Context, header []byte) { } // GetLink gets headers links -func (k Keeper) GetLink(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { +func (k Keeper) GetLink(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { store := ctx.KVStore(k.storeKey) result := store.Get(digest[:]) - return digestFromSlice(result) + digest, err := types.NewHash256Digest(result) + if err != nil { + return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil } // GetRelayGenesis returns the first digest in the relay -func (k Keeper) GetRelayGenesis(ctx sdk.Context) types.Hash256Digest { +func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { store := ctx.KVStore(k.storeKey) result := store.Get([]byte(types.RelayGenesisStorage)) - return digestFromSlice(result) + digest, err := types.NewHash256Digest(result) + if err != nil { + return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil } // SetRelayGenesis sets the first digest in the relay @@ -64,11 +66,15 @@ func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Diges } // GetBestKnownDigest returns the best known digest in the relay -func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { +func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { store := ctx.KVStore(k.storeKey) result := store.Get([]byte(types.BestKnownDigestStorage)) - return digestFromSlice(result) + digest, err := types.NewHash256Digest(result) + if err != nil { + return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil } // func (k Keeper) SetBestKnownDigest(ctx sdk.Context, bestKnownDigest types.Hash256Digest, digest types.Hash256Digest) { diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index ddd8b45b..bc7d09a1 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -29,12 +29,15 @@ func NewQuerier(keeper Keeper) sdk.Querier { func queryGetParent(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { digest, err := hex.DecodeString(path[0]) if err != nil || len(digest) != 32 { - panic("could not unhex argument string") + return []byte{}, types.ErrBadHex(types.DefaultCodespace) } var buf [32]byte copy(buf[:], digest[:32]) - parent := keeper.GetLink(ctx, buf) + parent, sdkErr := keeper.GetLink(ctx, buf) + if sdkErr != nil { + return []byte{}, sdkErr + } if bytes.Equal(parent[:], []byte{}) { return []byte{}, types.ErrUnknownBlock(types.DefaultCodespace) diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index fae0c0ca..3b141392 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -22,6 +22,12 @@ const ( // BadHeight occurs when a proposed descendant is below a proposed ancestor BadHeight sdk.CodeType = 104 + // BadHash256Digest occurs when a wrong-length hash256 digest is found + BadHash256Digest sdk.CodeType = 105 + + // BadHex occurs when a hex argument couldn't be deserialized + BadHex sdk.CodeType = 106 + // 200-block -- AddHeaders // RetargetOnExternal indicates a retarget was seen in AddHeaders @@ -128,3 +134,13 @@ func ErrNotHeaviestAncestor(codespace sdk.CodespaceType) sdk.Error { func ErrNotHeavier(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, NotHeavier, "New best hash does not have more work than previous") } + +// ErrBadHash256Digest throws an error +func ErrBadHash256Digest(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadHash256Digest, "Digest had wrong length") +} + +// ErrBadHex throws an error +func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, BadHex, "Bad hex string in query or msg") +} diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 82f246ae..9e21f69c 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -1,7 +1,19 @@ package types +import "fmt" + // Hash256Digest 32-byte double-sha2 digest -type Hash256Digest = [32]byte +type Hash256Digest [32]byte + +// NewHash256Digest instantiates a Hash256Digest from a byte slice +func NewHash256Digest(b []byte) (Hash256Digest, error) { + var h Hash256Digest + copied := copy(h[:], b) + if copied != 32 { + return h, fmt.Errorf("Expected 32 bytes in a Hash256 digest, got %d", copied) + } + return h, nil +} // Link is a link in the chain type Link struct { From f98b549c1c78e3e8922134cd8c6a2148bff76353 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 12 Nov 2019 10:48:11 -0800 Subject: [PATCH 013/148] refactor: keeper split into multiple prefixed stores --- golang/go.mod | 4 +- golang/go.sum | 49 ++++++++-------- golang/x/relay/alias.go | 14 +++-- golang/x/relay/genesis.go | 38 ++++++------- golang/x/relay/keeper/chain.go | 39 +++++++++++++ golang/x/relay/keeper/handler.go | 13 +---- golang/x/relay/keeper/headers.go | 43 +++++++++++++++ golang/x/relay/keeper/keeper.go | 95 +------------------------------- golang/x/relay/keeper/links.go | 16 ++++++ golang/x/relay/keeper/querier.go | 37 +------------ golang/x/relay/types/errors.go | 11 ++++ golang/x/relay/types/keys.go | 6 ++ golang/x/relay/types/types.go | 36 ++++++------ 13 files changed, 194 insertions(+), 207 deletions(-) create mode 100644 golang/x/relay/keeper/chain.go create mode 100644 golang/x/relay/keeper/headers.go create mode 100644 golang/x/relay/keeper/links.go diff --git a/golang/go.mod b/golang/go.mod index eb61d81b..3a4c6c23 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -4,13 +4,11 @@ go 1.12 require ( github.com/cosmos/cosmos-sdk v0.37.0 - github.com/gogo/protobuf v1.2.1 github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 - github.com/summa-tx/bitcoin-spv/golang v1.0.0 + github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 - google.golang.org/appengine v1.4.0 // indirect ) diff --git a/golang/go.sum b/golang/go.sum index 05d6da62..93d19a4c 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -1,6 +1,8 @@ bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 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= @@ -32,18 +34,20 @@ github.com/cosmos/cosmos-sdk v0.37.0 h1:S2I3NDGN2wqfGlY5KqkAHTpfezjhgeqDxrCxhlhd github.com/cosmos/cosmos-sdk v0.37.0/go.mod h1:3b/k/Zd+YDuttSmEJdNkxga1H5EIiDUhSYeErAHQN7A= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI= github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/ledger-cosmos-go v0.10.3 h1:Qhi5yTR5Pg1CaTpd00pxlGwNl4sFRdtK1J96OTjeFFc= github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= +github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= -github.com/cosmos/sdk-application-tutorial v0.37.0 h1:KMk6qsTTEfvb0Y6O6lCdQC8OryNmnANfscJ4g7Qw3a0= -github.com/cosmos/sdk-application-tutorial v0.37.0/go.mod h1:RvG8/WZFCSo/MVhKCp4U0PPnpzlzcUx+lZfAdkjqObs= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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= @@ -53,13 +57,16 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 h1:tT8iWCYw4uOem71yYA3htfH+LNopJvcqZQshm56G5L4= github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -70,6 +77,7 @@ github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -78,9 +86,12 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -88,6 +99,7 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/libp2p/go-buffer-pool v0.0.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= @@ -95,8 +107,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -104,7 +114,9 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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= github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1/go.mod h1:pXzZSDlN+HPzSdyIBnKNN9ptD9Hx7iZMWIJPTwo4FPE= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= @@ -130,8 +142,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb h1:LvNCMEj0FFZQYsxZb7o3xQPrtqOOB6lrTUOWshC+ZTs= -github.com/prometheus/procfs v0.0.0-20190328153300-af7bedc223fb/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.5 h1:Ly2UjURzxnsSYS0zI50fZ+srA+Fu7EbpV5hglvJvJG0= github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= @@ -144,8 +154,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -168,12 +176,10 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/summa-tx/bitcoin-spv v2.0.0+incompatible h1:so6fH4hvyZ7LI8mC9mLOiV5u+8gxFpsXG84g1dGyK7M= -github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918190112-a7c5660022bf h1:tdjIiV7L9za+ojwn8Eaevs1cod8y38/R9/bjQ1JSwgo= -github.com/summa-tx/bitcoin-spv/golang v0.0.0-20190918191218-559c9dda767c h1:Sq49DXoaUOpAF8lHsQFz2tFPwBI4o/wjPgNHBe11io0= -github.com/summa-tx/bitcoin-spv/golang v1.0.0 h1:zz3p7SQGOtMMqh5BwcUPUXsCACT3+E1Gjpxcqi80Yac= -github.com/summa-tx/bitcoin-spv/golang v1.0.0/go.mod h1:S0xvnR5vlpgFy2iaHEgcpVJYeJ9ohoMlE2yagtwoe9Y= -github.com/summa-tx/relays v0.0.0-20190918181406-3f7ee83f6828 h1:QusoX/pzugVlaLfDytFABL1iOU5hqata2o4u1BBHyRA= +github.com/summa-tx/bitcoin-spv/golang v1.1.0 h1:fpnC/LvOtMFCLrzjCmoTZyCGZr1/skgkJromSk4nBBU= +github.com/summa-tx/bitcoin-spv/golang v1.1.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4 h1:ygtjTMYRNTyJvZb2DlN5xnbc20Zw6XSAry4br43XiW0= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= @@ -195,12 +201,13 @@ github.com/tendermint/tendermint v0.31.5/go.mod h1:ymcPyWblXCplCPQjbOYbrF1fWnpsl github.com/tendermint/tendermint v0.32.1/go.mod h1:jmPDAKuNkev9793/ivn/fTBnfpA9mGBww8MPRNPNxnU= github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXHwyrV1QY= github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE= -github.com/tendermint/tendermint v0.32.3 h1:GEnWpGQ795h5oTFNbfBLsY0LW/CW2j6p6HtiYNfxsgg= github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -210,16 +217,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-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/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/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= @@ -238,34 +241,32 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190329044733-9eb1bfa1ce65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d h1:XB2jc5XQ9uhizGTS2vWcN01bc4dI6z3C4KY5MQm8SS8= -google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index ba1ab28a..c58d610b 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -34,9 +34,15 @@ type ( // Hash256Digest 32-byte double-sha2 digest Hash256Digest = types.Hash256Digest - // MsgSetLink is what is says on the tin - MsgSetLink = types.MsgSetLink + // Hash160Digest is a 20-byte ripemd160+sha2 hash + Hash160Digest = types.Hash160Digest - // QueryResGetParent is what is says on the tin - QueryResGetParent = types.QueryResGetParent + // RawHeader is an 80-byte raw header + RawHeader = types.RawHeader + + // BitcoinHeader is a parsed Bitcoin header + BitcoinHeader = types.BitcoinHeader + + // SPVProof is the base struct for an SPV proof + SPVProof = types.SPVProof ) diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index b6e504c6..25e7921a 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -1,56 +1,50 @@ package relay import ( - "encoding/hex" - "fmt" - - "github.com/summa-tx/relays/golang/x/relay/types" - sdk "github.com/cosmos/cosmos-sdk/types" + btcspv "github.com/summa-tx/bitcoin-spv/golang/btcspv" abci "github.com/tendermint/tendermint/abci/types" ) // GenesisState is the genesis state type GenesisState struct { - Headers []string `json:"headers"` - Links []types.Link `json:"links"` + Headers []btcspv.BitcoinHeader `json:"headers"` } // NewGenesisState instantiates a genesis state -func NewGenesisState(headers []string) GenesisState { - return GenesisState{Headers: headers, Links: []types.Link{}} +func NewGenesisState(headers []btcspv.BitcoinHeader) GenesisState { + return GenesisState{Headers: headers} } -// ValidateGenesis Validates a genesis state +// ValidateGenesis validates a genesis state func ValidateGenesis(data GenesisState) error { + raw := []byte{} for _, header := range data.Headers { - if len(header) != 160 { - return fmt.Errorf("Invalid header, bad length") - } - - _, err := hex.DecodeString(header) + _, err := header.Validate() if err != nil { return err } + raw = append(raw, header.Raw[:]...) } + + _, err := btcspv.ValidateHeaderChain(raw) + if err != nil { + return err + } + return nil } // DefaultGenesisState makes a default empty genesis state func DefaultGenesisState() GenesisState { return GenesisState{ - Headers: []string{}, - Links: []types.Link{}, + Headers: []btcspv.BitcoinHeader{}, } } // InitGenesis inits the app state based on the genesis state func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate { - for _, header := range data.Headers { - // can't fail if passes ValidateGenesis - h, _ := hex.DecodeString(header) - keeper.SetLink(ctx, h) - } + keeper.IngestHeaderChain(ctx, data.Headers) return []abci.ValidatorUpdate{} } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go new file mode 100644 index 00000000..d5ee8a5d --- /dev/null +++ b/golang/x/relay/keeper/chain.go @@ -0,0 +1,39 @@ +package keeper + +import ( + "github.com/summa-tx/relays/golang/x/relay/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" +) + +// GetRelayGenesis returns the first digest in the relay +func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { + store := ctx.KVStore(k.storeKey) + result := store.Get([]byte(types.RelayGenesisStorage)) + + digest, err := btcspv.NewHash256Digest(result) + if err != nil { + return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil +} + +// SetRelayGenesis sets the first digest in the relay +func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := ctx.KVStore(k.storeKey) + store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) +} + +// GetBestKnownDigest returns the best known digest in the relay +func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { + store := ctx.KVStore(k.storeKey) + result := store.Get([]byte(types.BestKnownDigestStorage)) + + digest, err := btcspv.NewHash256Digest(result) + if err != nil { + return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil +} diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 7e2c9b4a..8c62fd59 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -1,29 +1,20 @@ package keeper import ( - "encoding/hex" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/summa-tx/relays/golang/x/relay/types" ) // NewHandler returns a handler for "nameservice" type messages. func NewHandler(keeper Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { switch msg := msg.(type) { - case types.MsgSetLink: - return handleMsgSetLink(ctx, keeper, msg) + // case types.MsgSetLink: + // return handleMsgSetLink(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() } } } - -// Handle a message to set name -func handleMsgSetLink(ctx sdk.Context, keeper Keeper, msg types.MsgSetLink) sdk.Result { - b, _ := hex.DecodeString(msg.Header) - keeper.SetLink(ctx, b) // If so, set the name to the value specified in the msg.//s - return sdk.Result{} // return -} diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go new file mode 100644 index 00000000..5c124336 --- /dev/null +++ b/golang/x/relay/keeper/headers.go @@ -0,0 +1,43 @@ +package keeper + +import ( + "github.com/summa-tx/bitcoin-spv/golang/btcspv" + "github.com/summa-tx/relays/golang/x/relay/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { + return k.getPrefixStore(ctx, types.HeaderStorePrefix) +} + +func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { + // store := k.getHeaderStore(ctx) + + // TODO: store the header blob as serialized something +} + +// IngestHeaderChain ingests headers +func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { + + raw := []byte{} + for _, header := range headers { + _, err := header.Validate() + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + raw = append(raw, header.Raw[:]...) + } + + _, err := btcspv.ValidateHeaderChain(raw) + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + for _, header := range headers { + k.setLink(ctx, header) + k.ingestHeader(ctx, header) + } + + return nil +} diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index a4a14688..ba3e2396 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -1,11 +1,9 @@ package keeper import ( - "github.com/summa-tx/relays/golang/x/relay/types" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine @@ -22,93 +20,6 @@ func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { } } -func (k Keeper) setLink(ctx sdk.Context, header []byte) { - digest := btcspv.Hash256(header) - parent := btcspv.ExtractPrevBlockHashLE(header) - store := ctx.KVStore(k.storeKey) - store.Set(digest[:], parent[:]) -} - -// SetLink sets a header parent -func (k Keeper) SetLink(ctx sdk.Context, header []byte) { - // TODO: Remove this in favor of fully validating add headers - k.setLink(ctx, header) +func (k Keeper) getPrefixStore(ctx sdk.Context, namespace string) sdk.KVStore { + return prefix.NewStore(ctx.KVStore(k.storeKey), []byte(namespace)) } - -// GetLink gets headers links -func (k Keeper) GetLink(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { - store := ctx.KVStore(k.storeKey) - result := store.Get(digest[:]) - digest, err := types.NewHash256Digest(result) - if err != nil { - return digest, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil -} - -// GetRelayGenesis returns the first digest in the relay -func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - store := ctx.KVStore(k.storeKey) - result := store.Get([]byte(types.RelayGenesisStorage)) - - digest, err := types.NewHash256Digest(result) - if err != nil { - return digest, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil -} - -// SetRelayGenesis sets the first digest in the relay -func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state - store := ctx.KVStore(k.storeKey) - store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) -} - -// GetBestKnownDigest returns the best known digest in the relay -func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { - store := ctx.KVStore(k.storeKey) - result := store.Get([]byte(types.BestKnownDigestStorage)) - - digest, err := types.NewHash256Digest(result) - if err != nil { - return digest, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil -} - -// func (k Keeper) SetBestKnownDigest(ctx sdk.Context, bestKnownDigest types.Hash256Digest, digest types.Hash256Digest) { -// link := k.GetLink(ctx, digest) -// link.BestKnownDigest = bestKnownDigest -// k.SetLink(ctx, bestKnownDigest) -// } -// -// func (k Keeper) GetLastReorgCommonAncestor(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { -// return k.GetLink(ctx, digest).LastReorgCommonAncestor -// } -// -// func (k Keeper) SetLastReorgCommonAncestor(ctx sdk.Context, lastReorgCommonAncestor types.Hash256Digest, digest types.Hash256Digest) { -// link := k.GetLink(ctx, digest) -// link.LastReorgCommonAncestor = lastReorgCommonAncestor -// k.SetLink(ctx, lastReorgCommonAncestor) -// } -// -// func (k Keeper) GetPreviousBlock(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { -// return k.GetLink(ctx, digest).PreviousBlock -// } -// -// func (k Keeper) SetPreviousBlock(ctx sdk.Context, previousBlock types.Hash256Digest, digest types.Hash256Digest) { -// link := k.GetLink(ctx, digest) -// link.PreviousBlock = previousBlock -// k.SetLink(ctx, previousBlock) -// } -// -// func (k Keeper) GetBlockHeight(ctx sdk.Context, digest types.Hash256Digest) types.Hash256Digest { -// return k.GetLink(ctx, digest).BlockHeight -// } -// -// func (k Keeper) SetBlockHeight(ctx sdk.Context, blockHeight types.Hash256Digest, digest types.Hash256Digest) { -// link := k.GetLink(ctx, digest) -// link.BlockHeight = blockHeight -// k.SetLink(ctx, blockHeight) -// } diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go new file mode 100644 index 00000000..8c9c974c --- /dev/null +++ b/golang/x/relay/keeper/links.go @@ -0,0 +1,16 @@ +package keeper + +import ( + "github.com/summa-tx/relays/golang/x/relay/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) getLinkStore(ctx sdk.Context) sdk.KVStore { + return k.getPrefixStore(ctx, types.LinkStorePrefix) +} + +func (k Keeper) setLink(ctx sdk.Context, header types.BitcoinHeader) { + store := k.getLinkStore(ctx) + store.Set(header.HashLE[:], header.PrevHash[:]) +} diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index bc7d09a1..acde6a20 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -1,12 +1,6 @@ package keeper import ( - "bytes" - "encoding/hex" - - "github.com/summa-tx/relays/golang/x/relay/types" - - "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -18,37 +12,10 @@ const QueryGetParent = "getparent" func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - case QueryGetParent: - return queryGetParent(ctx, path[1:], req, keeper) + // case QueryGetParent: + // return queryGetParent(ctx, path[1:], req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown nameservice query endpoint") } } } - -func queryGetParent(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { - digest, err := hex.DecodeString(path[0]) - if err != nil || len(digest) != 32 { - return []byte{}, types.ErrBadHex(types.DefaultCodespace) - } - - var buf [32]byte - copy(buf[:], digest[:32]) - parent, sdkErr := keeper.GetLink(ctx, buf) - if sdkErr != nil { - return []byte{}, sdkErr - } - - if bytes.Equal(parent[:], []byte{}) { - return []byte{}, types.ErrUnknownBlock(types.DefaultCodespace) - } - - response := types.QueryResGetParent{ - Digest: hex.EncodeToString(digest), - Parent: hex.EncodeToString(parent[:])} - res, err := codec.MarshalJSONIndent(keeper.cdc, response) - if err != nil { - panic("could not marshal result to JSON") - } - return res, nil -} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 3b141392..d9ccc9f8 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -10,6 +10,9 @@ const ( // 100-block -- shared errors + // UnknownError is an unknown error + UnknownError sdk.CodeType = 100 + // BadHeaderLength means Header array length not divisible by 80 BadHeaderLength sdk.CodeType = 101 @@ -28,6 +31,9 @@ const ( // BadHex occurs when a hex argument couldn't be deserialized BadHex sdk.CodeType = 106 + // BitcoinSPV is the code for errors bubbled up from Bitcoin SPV + BitcoinSPV sdk.CodeType = 107 + // 200-block -- AddHeaders // RetargetOnExternal indicates a retarget was seen in AddHeaders @@ -144,3 +150,8 @@ func ErrBadHash256Digest(codespace sdk.CodespaceType) sdk.Error { func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, BadHex, "Bad hex string in query or msg") } + +// FromBTCSPVError converts a btcutils error into an sdk error +func FromBTCSPVError(codespace sdk.CodespaceType, err error) sdk.Error { + return sdk.NewError(codespace, BitcoinSPV, err.Error()) +} diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index b236410c..37cb3bd3 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -7,6 +7,12 @@ const ( // StoreKey to be used when creating the KVStore StoreKey = ModuleName + // LinkStorePrefix to be used when accessing links + LinkStorePrefix = ModuleName + "links-" + + // HeaderStorePrefix to be used when accessing headers + HeaderStorePrefix = ModuleName + "headers-" + // RelayGenesisStorage is the storage key for the relay genesis digest RelayGenesisStorage = "RelayGenesis" diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 9e21f69c..1a3f5678 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -1,22 +1,26 @@ package types -import "fmt" +import ( + "github.com/summa-tx/bitcoin-spv/golang/btcspv" +) // Hash256Digest 32-byte double-sha2 digest -type Hash256Digest [32]byte +type Hash256Digest = btcspv.Hash256Digest -// NewHash256Digest instantiates a Hash256Digest from a byte slice -func NewHash256Digest(b []byte) (Hash256Digest, error) { - var h Hash256Digest - copied := copy(h[:], b) - if copied != 32 { - return h, fmt.Errorf("Expected 32 bytes in a Hash256 digest, got %d", copied) - } - return h, nil -} +// Hash160Digest is a 20-byte ripemd160+sha2 hash +type Hash160Digest = btcspv.Hash160Digest -// Link is a link in the chain -type Link struct { - Digest string `json:"digest"` - Parent string `json:"parent"` -} +// RawHeader is an 80-byte raw header +type RawHeader = btcspv.RawHeader + +// BitcoinHeader is a parsed Bitcoin header +type BitcoinHeader = btcspv.BitcoinHeader + +// SPVProof is the base struct for an SPV proof +type SPVProof = btcspv.SPVProof + +// // Link is a link in the chain +// type Link struct { +// Digest string `json:"digest"` +// Parent string `json:"parent"` +// } From 578c6696c1c9c7fca916163379cbf1ccf6c9de24 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 12 Nov 2019 16:57:14 -0800 Subject: [PATCH 014/148] feature: continue working on link and header store --- golang/README.md | 2 +- golang/go.mod | 2 +- golang/go.sum | 5 ++ golang/x/relay/keeper/headers.go | 79 ++++++++++++++++++++++++-------- golang/x/relay/keeper/links.go | 11 ++++- 5 files changed, 76 insertions(+), 23 deletions(-) diff --git a/golang/README.md b/golang/README.md index 6c01b673..bb7833cf 100644 --- a/golang/README.md +++ b/golang/README.md @@ -1,9 +1,9 @@ ## TODOs: 1. Migrate solidity logic +1. Add iterator for links 1. Add reasonable genesis state 1. Add state export -1. Consider storing more state than solidity does ## How to add a view function 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` diff --git a/golang/go.mod b/golang/go.mod index 3a4c6c23..f22bfc89 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -7,7 +7,7 @@ require ( github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 - github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4 + github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 diff --git a/golang/go.sum b/golang/go.sum index 93d19a4c..8000c395 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -176,10 +176,15 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/summa-tx/bitcoin-spv v2.0.0+incompatible h1:so6fH4hvyZ7LI8mC9mLOiV5u+8gxFpsXG84g1dGyK7M= github.com/summa-tx/bitcoin-spv/golang v1.1.0 h1:fpnC/LvOtMFCLrzjCmoTZyCGZr1/skgkJromSk4nBBU= github.com/summa-tx/bitcoin-spv/golang v1.1.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4 h1:ygtjTMYRNTyJvZb2DlN5xnbc20Zw6XSAry4br43XiW0= github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112011044-15eeb47227b4/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112210231-284f474dd1a8 h1:M9kZdivYUXYQ2MkZsax89tW54oKXE0/hM3BCb6LMtR0= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112210231-284f474dd1a8/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07 h1:5znRqPaGWwJTgI7eb8yJdHekr6rREwAp9/fdLjgZyD8= +github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 5c124336..e00b299a 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -12,32 +12,71 @@ func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { } func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { - // store := k.getHeaderStore(ctx) + store := k.getHeaderStore(ctx) - // TODO: store the header blob as serialized something + buf := k.cdc.MustMarshalBinaryBare(header) + store.Set(header.HashLE[:], buf) } -// IngestHeaderChain ingests headers -func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { +// GetHeader retrieves a header from the store using its LE diges +func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.BitcoinHeader { + var header types.BitcoinHeader + store := k.getHeaderStore(ctx) - raw := []byte{} - for _, header := range headers { - _, err := header.Validate() - if err != nil { - return types.FromBTCSPVError(types.DefaultCodespace, err) - } - raw = append(raw, header.Raw[:]...) - } + buf := store.Get(digestLE[:]) + k.cdc.MustUnmarshalBinaryBare(buf, &header) - _, err := btcspv.ValidateHeaderChain(raw) - if err != nil { - return types.FromBTCSPVError(types.DefaultCodespace, err) - } + return header +} - for _, header := range headers { - k.setLink(ctx, header) - k.ingestHeader(ctx, header) - } +func (k Keeper) ingestHeaders(ctx sdk.Context, anchor types.BitcoinHeader, headers []types.BitcoinHeader) sdk.Error { + target := btcspv.ExtractTarget(anchor.Raw) + // TODO: validation + // // Make the raw chain + // raw := []byte{} + // for i, header := range headers { + // _, err := header.Validate() + // if err != nil { + // return types.FromBTCSPVError(types.DefaultCodespace, err) + // } + // /// Check that height is consistent throughout + // if i > 0 { + // if headers[i-1].Height != header.Height-1 { + // return types.ErrBadHeight(types.DefaultCodespace) + // } + // if btcspv.ExtractTarget(header.Raw) != target { + // return types.ErrUnexptectedRetarget(types.DefaultCodespace) + // } + // } + // raw = append(raw, header.Raw[:]...) + // } + // + // // Then validate the chain + // _, err := btcspv.ValidateHeaderChain(raw) + // if err != nil { + // return types.FromBTCSPVError(types.DefaultCodespace, err) + // } + // + // for _, header := range headers { + // k.setLink(ctx, header) + // k.ingestHeader(ctx, header) + // } + return nil +} +func (k Keeper) ingestDiffChange(ctx sdk.Context, anchor types.BitcoinHeader, headers []types.BitcoinHeader) sdk.Error { + // TODO return nil } + +// IngestHeaderChain ingests a chain of headers +func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { + + // Find the anchor in our store + anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + + if anchor.Height%2016 == 0 { + return k.ingestDiffChange(ctx, anchor, headers) + } + return k.ingestHeaders(ctx, anchor, headers) +} diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index 8c9c974c..11dc0d14 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -1,6 +1,7 @@ package keeper import ( + btcspv "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -12,5 +13,13 @@ func (k Keeper) getLinkStore(ctx sdk.Context) sdk.KVStore { func (k Keeper) setLink(ctx sdk.Context, header types.BitcoinHeader) { store := k.getLinkStore(ctx) - store.Set(header.HashLE[:], header.PrevHash[:]) + store.Set(header.HashLE[:], header.PrevHashLE[:]) +} + +func (k Keeper) getLink(ctx sdk.Context, digestLE types.Hash256Digest) types.Hash256Digest { + store := k.getLinkStore(ctx) + buf := store.Get(digestLE[:]) + // Can only fail if data store is corrupt + parentHashLE, _ := btcspv.NewHash256Digest(buf) + return parentHashLE } From 5afeadd0d64f507b208d29c96e23d95992435adf Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 18 Nov 2019 14:23:45 -0800 Subject: [PATCH 015/148] feature: header chain validation for ingestion --- golang/go.mod | 2 +- golang/go.sum | 5 ++ golang/x/relay/keeper/headers.go | 150 ++++++++++++++++++++++--------- golang/x/relay/types/errors.go | 12 +-- 4 files changed, 118 insertions(+), 51 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index f22bfc89..363a46ac 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -7,7 +7,7 @@ require ( github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 - github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07 + github.com/summa-tx/bitcoin-spv/golang v1.2.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 diff --git a/golang/go.sum b/golang/go.sum index 8000c395..d88e6ea4 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -54,6 +54,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -185,6 +186,8 @@ github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112210231-284f474dd1a8 h1:M github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191112210231-284f474dd1a8/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07 h1:5znRqPaGWwJTgI7eb8yJdHekr6rREwAp9/fdLjgZyD8= github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.2.0 h1:d9VSHSb4HMQOYshbOTRh26g5yU4tUiBIn4cr9hlB2CE= +github.com/summa-tx/bitcoin-spv/golang v1.2.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= @@ -219,6 +222,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -245,6 +249,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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= diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index e00b299a..017875aa 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -1,6 +1,8 @@ package keeper import ( + "math/big" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" @@ -11,6 +13,21 @@ func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.HeaderStorePrefix) } +func compareTargets(full, truncated sdk.Uint) bool { + // dirty hacks. sdk.Uint doesn't give us easy access to the underlying + a, _ := full.MarshalAmino() + e, _ := truncated.MarshalAmino() + actualBI := new(big.Int) + actualBI.SetString(a, 0) + expectedBI := new(big.Int) + expectedBI.SetString(e, 0) + + res := new(big.Int) + res.And(actualBI, expectedBI) + + return expectedBI == res +} + func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store := k.getHeaderStore(ctx) @@ -18,6 +35,11 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store.Set(header.HashLE[:], buf) } +// HasHeader checks if a header is in the store +func (k Keeper) HasHeader(ctx sdk.Context, digestLE types.Hash256Digest) bool { + return k.getHeaderStore(ctx).Has(digestLE[:]) +} + // GetHeader retrieves a header from the store using its LE diges func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.BitcoinHeader { var header types.BitcoinHeader @@ -29,54 +51,102 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.B return header } -func (k Keeper) ingestHeaders(ctx sdk.Context, anchor types.BitcoinHeader, headers []types.BitcoinHeader) sdk.Error { +func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { + if !k.HasHeader(ctx, headers[0].PrevHashLE) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + + anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + prev := anchor // scratchpad, we change this later + target := btcspv.ExtractTarget(anchor.Raw) - // TODO: validation - // // Make the raw chain - // raw := []byte{} - // for i, header := range headers { - // _, err := header.Validate() - // if err != nil { - // return types.FromBTCSPVError(types.DefaultCodespace, err) - // } - // /// Check that height is consistent throughout - // if i > 0 { - // if headers[i-1].Height != header.Height-1 { - // return types.ErrBadHeight(types.DefaultCodespace) - // } - // if btcspv.ExtractTarget(header.Raw) != target { - // return types.ErrUnexptectedRetarget(types.DefaultCodespace) - // } - // } - // raw = append(raw, header.Raw[:]...) - // } - // - // // Then validate the chain - // _, err := btcspv.ValidateHeaderChain(raw) - // if err != nil { - // return types.FromBTCSPVError(types.DefaultCodespace, err) - // } - // - // for _, header := range headers { - // k.setLink(ctx, header) - // k.ingestHeader(ctx, header) - // } - return nil -} -func (k Keeper) ingestDiffChange(ctx sdk.Context, anchor types.BitcoinHeader, headers []types.BitcoinHeader) sdk.Error { - // TODO + // allocate memory for raw anchor + all headers + raw := make([]byte, 80*(len(headers)+1)) + copy(raw[0:80], anchor.Raw[:]) + + // Make the raw chain + // TODO: test behavior + for i, header := range headers { + _, err := header.Validate() + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + // ensure height changes as expected + if prev.Height != header.Height-1 { + return types.ErrBadHeight(types.DefaultCodespace) + } + + // ensure target doesn't change + if btcspv.ExtractTarget(header.Raw) != target { + return types.ErrUnexptectedRetarget(types.DefaultCodespace) + } + + // copy header raw into a bytearray + offset := 80 * (i + 1) + copy(raw[offset:offset+80], header.Raw[:]) + prev = header + } + + // Then validate the chain + _, err := btcspv.ValidateHeaderChain(raw) + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + for _, header := range headers { + k.setLink(ctx, header) + k.ingestHeader(ctx, header) + } return nil } -// IngestHeaderChain ingests a chain of headers -func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { +func (k Keeper) ingestDiffChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { + if !k.HasHeader(ctx, prevEpochStartLE) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + if !k.HasHeader(ctx, headers[0].PrevHashLE) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } // Find the anchor in our store + prevEpochStart := k.GetHeader(ctx, prevEpochStartLE) anchor := k.GetHeader(ctx, headers[0].PrevHashLE) - if anchor.Height%2016 == 0 { - return k.ingestDiffChange(ctx, anchor, headers) + if anchor.Height%2016 != 2015 { + return types.ErrWrongEnd(types.DefaultCodespace) + } + if anchor.Height != prevEpochStart.Height+2015 || anchor.Height < prevEpochStart.Height { + return types.ErrWrongStart(types.DefaultCodespace) + } + if btcspv.ExtractDifficulty(anchor.Raw) != btcspv.ExtractDifficulty(prevEpochStart.Raw) { + return types.ErrPeriodMismatch(types.DefaultCodespace) + } + + // calculated target + expectedTarget := btcspv.RetargetAlgorithm( + btcspv.ExtractTarget(prevEpochStart.Raw), + btcspv.ExtractTimestamp(prevEpochStart.Raw), + btcspv.ExtractTimestamp(anchor.Raw)) + + // Observed target in the new period start header + actualTarget := btcspv.ExtractTarget(headers[0].Raw) + + if !compareTargets(expectedTarget, actualTarget) { + return types.ErrBadRetarget(types.DefaultCodespace) } - return k.ingestHeaders(ctx, anchor, headers) + + return k.ingestHeaders(ctx, headers) +} + +// IngestHeaderChain ingests a chain of headers +func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { + // Find the anchor in our store + return k.ingestHeaders(ctx, headers) +} + +// IngestDiffChange ingests a chain of headers +func (k Keeper) IngestDiffChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { + return k.ingestDiffChange(ctx, prevEpochStartLE, headers) } diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index d9ccc9f8..734ddcd0 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -36,14 +36,11 @@ const ( // 200-block -- AddHeaders - // RetargetOnExternal indicates a retarget was seen in AddHeaders - RetargetOnExternal sdk.CodeType = 201 - // UnexptectedRetarget indicates a retarget was seen during AddHeaders loop - UnexptectedRetarget sdk.CodeType = 202 + UnexptectedRetarget sdk.CodeType = 201 // BadLink indicates a broken link was found in the header array during AddHeaders loop - BadLink sdk.CodeType = 203 + BadLink sdk.CodeType = 202 // 300-block AddHeadersWithRetarget @@ -91,11 +88,6 @@ func ErrBadHeight(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, BadHeight, "A descendant height is below the ancestor height") } -// ErrRetargetOnExternal throws an error -func ErrRetargetOnExternal(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, RetargetOnExternal, "Unexpected retarget on external call") -} - // ErrUnexptectedRetarget throws an error func ErrUnexptectedRetarget(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, UnexptectedRetarget, "Target changed unexpectedly") From ec44fb5a196a7b088076d741d9ec64d512a15c02 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 18 Nov 2019 14:35:13 -0800 Subject: [PATCH 016/148] bug: reintroduce concept of external call --- golang/x/relay/keeper/headers.go | 48 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 017875aa..2c9dddd0 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -13,6 +13,22 @@ func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.HeaderStorePrefix) } +// HasHeader checks if a header is in the store +func (k Keeper) HasHeader(ctx sdk.Context, digestLE types.Hash256Digest) bool { + return k.getHeaderStore(ctx).Has(digestLE[:]) +} + +// GetHeader retrieves a header from the store using its LE diges +func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.BitcoinHeader { + var header types.BitcoinHeader + store := k.getHeaderStore(ctx) + + buf := store.Get(digestLE[:]) + k.cdc.MustUnmarshalBinaryBare(buf, &header) + + return header +} + func compareTargets(full, truncated sdk.Uint) bool { // dirty hacks. sdk.Uint doesn't give us easy access to the underlying a, _ := full.MarshalAmino() @@ -35,23 +51,7 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store.Set(header.HashLE[:], buf) } -// HasHeader checks if a header is in the store -func (k Keeper) HasHeader(ctx sdk.Context, digestLE types.Hash256Digest) bool { - return k.getHeaderStore(ctx).Has(digestLE[:]) -} - -// GetHeader retrieves a header from the store using its LE diges -func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.BitcoinHeader { - var header types.BitcoinHeader - store := k.getHeaderStore(ctx) - - buf := store.Get(digestLE[:]) - k.cdc.MustUnmarshalBinaryBare(buf, &header) - - return header -} - -func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { +func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { if !k.HasHeader(ctx, headers[0].PrevHashLE) { return types.ErrUnknownBlock(types.DefaultCodespace) } @@ -59,7 +59,11 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader) sd anchor := k.GetHeader(ctx, headers[0].PrevHashLE) prev := anchor // scratchpad, we change this later - target := btcspv.ExtractTarget(anchor.Raw) + // On internal call, use the header chain target + expectedTarget := btcspv.ExtractTarget(anchor.Raw) + if internal { + expectedTarget = btcspv.ExtractTarget(headers[0].Raw) + } // allocate memory for raw anchor + all headers raw := make([]byte, 80*(len(headers)+1)) @@ -78,8 +82,8 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader) sd return types.ErrBadHeight(types.DefaultCodespace) } - // ensure target doesn't change - if btcspv.ExtractTarget(header.Raw) != target { + // ensure expectedTarget doesn't change + if btcspv.ExtractTarget(header.Raw) != expectedTarget { return types.ErrUnexptectedRetarget(types.DefaultCodespace) } @@ -137,13 +141,13 @@ func (k Keeper) ingestDiffChange(ctx sdk.Context, prevEpochStartLE types.Hash256 return types.ErrBadRetarget(types.DefaultCodespace) } - return k.ingestHeaders(ctx, headers) + return k.ingestHeaders(ctx, headers, true) } // IngestHeaderChain ingests a chain of headers func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { // Find the anchor in our store - return k.ingestHeaders(ctx, headers) + return k.ingestHeaders(ctx, headers, false) } // IngestDiffChange ingests a chain of headers From 092fc52ae0ddef46902d9a123b2b61b4ddd12453 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 18 Nov 2019 14:58:37 -0800 Subject: [PATCH 017/148] feature: better genesis state handling --- golang/x/relay/genesis.go | 19 ++++++++++++---- golang/x/relay/keeper/chain.go | 39 ++++++++++++++++++++++++++++---- golang/x/relay/keeper/headers.go | 17 +++++++------- golang/x/relay/types/errors.go | 8 +++++++ golang/x/relay/types/keys.go | 7 ++++-- 5 files changed, 70 insertions(+), 20 deletions(-) diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index 25e7921a..af16680a 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -1,6 +1,8 @@ package relay import ( + "errors" + sdk "github.com/cosmos/cosmos-sdk/types" btcspv "github.com/summa-tx/bitcoin-spv/golang/btcspv" abci "github.com/tendermint/tendermint/abci/types" @@ -8,12 +10,13 @@ import ( // GenesisState is the genesis state type GenesisState struct { - Headers []btcspv.BitcoinHeader `json:"headers"` + Headers []btcspv.BitcoinHeader `json:"headers"` + PeriodStart btcspv.BitcoinHeader `json:"periodStart"` } // NewGenesisState instantiates a genesis state -func NewGenesisState(headers []btcspv.BitcoinHeader) GenesisState { - return GenesisState{Headers: headers} +func NewGenesisState(headers []btcspv.BitcoinHeader, periodStart btcspv.BitcoinHeader) GenesisState { + return GenesisState{Headers: headers, PeriodStart: periodStart} } // ValidateGenesis validates a genesis state @@ -32,19 +35,25 @@ func ValidateGenesis(data GenesisState) error { return err } + if data.PeriodStart.Height != (data.Headers[0].Height - (data.Headers[0].Height % 2016)) { + return errors.New("period start has incorrect height") + } + return nil } // DefaultGenesisState makes a default empty genesis state func DefaultGenesisState() GenesisState { return GenesisState{ - Headers: []btcspv.BitcoinHeader{}, + Headers: []btcspv.BitcoinHeader{}, + PeriodStart: btcspv.BitcoinHeader{}, } } // InitGenesis inits the app state based on the genesis state func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate { - keeper.IngestHeaderChain(ctx, data.Headers) + keeper.SetGenesisState(data.Headers[0], data.PeriodStart) + keeper.IngestHeaderChain(ctx, data.Headers[1:]) return []abci.ValidatorUpdate{} } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index d5ee8a5d..de3acce9 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -7,9 +7,31 @@ import ( "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) +func (k Keeper) getChainStore(ctx sdk.Context) sdk.KVStore { + return k.getPrefixStore(ctx, types.ChainStorePrefix) +} + +// SetGenesisState sets the genesis state +func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { + store := k.getChainStore(ctx) + + if store.Has([]byte(types.RelayGenesisStorage)) { + return types.ErrAlreadyInit(types.DefaultCodespace) + } + + k.ingestHeader(ctx, genesis) + k.ingestHeader(ctx, epochStart) + + k.setRelayGenesis(ctx, genesis.HashLE) + k.setBestKnownDigest(ctx, genesis.HashLE) + + return nil + +} + // GetRelayGenesis returns the first digest in the relay func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - store := ctx.KVStore(k.storeKey) + store := k.getChainStore(ctx) result := store.Get([]byte(types.RelayGenesisStorage)) digest, err := btcspv.NewHash256Digest(result) @@ -19,16 +41,23 @@ func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error return digest, nil } -// SetRelayGenesis sets the first digest in the relay -func (k Keeper) SetRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { +// setRelayGenesis sets the first digest in the relay +func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { // TODO: Remove this in favor of Genesis state - store := ctx.KVStore(k.storeKey) + store := k.getChainStore(ctx) store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) } +// setBestKnownDigest sets the first digest in the relay +func (k Keeper) setBestKnownDigest(ctx sdk.Context, bestKnown types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := k.getChainStore(ctx) + store.Set([]byte(types.BestKnownDigestStorage), bestKnown[:]) +} + // GetBestKnownDigest returns the best known digest in the relay func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { - store := ctx.KVStore(k.storeKey) + store := k.getChainStore(ctx) result := store.Get([]byte(types.BestKnownDigestStorage)) digest, err := btcspv.NewHash256Digest(result) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 2c9dddd0..08530dbb 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -29,19 +29,20 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.B return header } +// compareTargets compares Bitcoin truncated and full-length targets func compareTargets(full, truncated sdk.Uint) bool { // dirty hacks. sdk.Uint doesn't give us easy access to the underlying - a, _ := full.MarshalAmino() - e, _ := truncated.MarshalAmino() - actualBI := new(big.Int) - actualBI.SetString(a, 0) - expectedBI := new(big.Int) - expectedBI.SetString(e, 0) + f, _ := full.MarshalAmino() + t, _ := truncated.MarshalAmino() + fullBI := new(big.Int) + fullBI.SetString(f, 0) + truncatedBI := new(big.Int) + truncatedBI.SetString(t, 0) res := new(big.Int) - res.And(actualBI, expectedBI) + res.And(fullBI, truncatedBI) - return expectedBI == res + return truncatedBI.Cmp(res) == 0 } func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 734ddcd0..92c3ab44 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -34,6 +34,9 @@ const ( // BitcoinSPV is the code for errors bubbled up from Bitcoin SPV BitcoinSPV sdk.CodeType = 107 + // AlreadyInit is the code for a second attempt to init the relay + AlreadyInit sdk.CodeType = 108 + // 200-block -- AddHeaders // UnexptectedRetarget indicates a retarget was seen during AddHeaders loop @@ -143,6 +146,11 @@ func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, BadHex, "Bad hex string in query or msg") } +// ErrAlreadyInit throws an error +func ErrAlreadyInit(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, AlreadyInit, "Relay has already set genesis state") +} + // FromBTCSPVError converts a btcutils error into an sdk error func FromBTCSPVError(codespace sdk.CodespaceType, err error) sdk.Error { return sdk.NewError(codespace, BitcoinSPV, err.Error()) diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index 37cb3bd3..579eaf08 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -8,10 +8,13 @@ const ( StoreKey = ModuleName // LinkStorePrefix to be used when accessing links - LinkStorePrefix = ModuleName + "links-" + LinkStorePrefix = ModuleName + "-links-" // HeaderStorePrefix to be used when accessing headers - HeaderStorePrefix = ModuleName + "headers-" + HeaderStorePrefix = ModuleName + "-headers-" + + // ChainStorePrefix to be used when accessing chain metadata + ChainStorePrefix = ModuleName + "-chain-" // RelayGenesisStorage is the storage key for the relay genesis digest RelayGenesisStorage = "RelayGenesis" From cd32287d60af9c2cb8f2d24796ea4f55485e0a05 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 18 Nov 2019 15:25:21 -0800 Subject: [PATCH 018/148] feature: Reorg and Extension events --- golang/x/relay/keeper/chain.go | 13 +++++++++++++ golang/x/relay/keeper/headers.go | 11 +++++++++++ golang/x/relay/types/events.go | 17 +++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 golang/x/relay/types/events.go diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index de3acce9..49908851 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -1,6 +1,8 @@ package keeper import ( + "encoding/hex" + "github.com/summa-tx/relays/golang/x/relay/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -11,6 +13,17 @@ func (k Keeper) getChainStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.ChainStorePrefix) } +func (k Keeper) emitReorg(ctx sdk.Context, prev, new, lca types.BitcoinHeader) { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeReorg, + sdk.NewAttribute(types.AttributeKeyPreviousBest, "0x"+hex.EncodeToString(prev.HashLE[:])), + sdk.NewAttribute(types.AttributeKeyNewBest, "0x"+hex.EncodeToString(new.HashLE[:])), + sdk.NewAttribute(types.AttributeKeyLatestCommon, "0x"+hex.EncodeToString(lca.HashLE[:])), + ), + ) +} + // SetGenesisState sets the genesis state func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { store := k.getChainStore(ctx) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 08530dbb..e9b12bda 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -1,6 +1,7 @@ package keeper import ( + "encoding/hex" "math/big" "github.com/summa-tx/bitcoin-spv/golang/btcspv" @@ -13,6 +14,16 @@ func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.HeaderStorePrefix) } +func (k Keeper) emitExtension(ctx sdk.Context, first, last types.BitcoinHeader) { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeExtension, + sdk.NewAttribute(types.AttributeKeyFirstBlock, "0x"+hex.EncodeToString(first.HashLE[:])), + sdk.NewAttribute(types.AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.HashLE[:])), + ), + ) +} + // HasHeader checks if a header is in the store func (k Keeper) HasHeader(ctx sdk.Context, digestLE types.Hash256Digest) bool { return k.getHeaderStore(ctx).Has(digestLE[:]) diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go new file mode 100644 index 00000000..1cad51d3 --- /dev/null +++ b/golang/x/relay/types/events.go @@ -0,0 +1,17 @@ +package types + +// Relay module event types +const ( + EventTypeExtension = "extension" + EventTypeReorg = "reorg" + + AttributeKeyFirstBlock = "first_block" + AttributeKeyLastBlock = "last_block" + + AttributeKeyPreviousBest = "previous_best" + AttributeKeyNewBest = "new_best" + AttributeKeyLatestCommon = "latest_common_ancestor" +) + +// event Extension(bytes32 indexed _first, bytes32 indexed _last); +// event Reorg(bytes32 indexed _from, bytes32 indexed _to, bytes32 indexed _gcd); From e01b519c4c2789737b749d44f9e1e562995faf88 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 19 Nov 2019 08:54:35 -0800 Subject: [PATCH 019/148] refactor: break out events instantiation into the types module --- golang/x/relay/genesis.go | 2 +- golang/x/relay/keeper/chain.go | 11 +---------- golang/x/relay/keeper/headers.go | 9 +-------- golang/x/relay/types/events.go | 26 ++++++++++++++++++++++++-- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index af16680a..c92b426f 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -52,7 +52,7 @@ func DefaultGenesisState() GenesisState { // InitGenesis inits the app state based on the genesis state func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate { - keeper.SetGenesisState(data.Headers[0], data.PeriodStart) + keeper.SetGenesisState(ctx, data.Headers[0], data.PeriodStart) keeper.IngestHeaderChain(ctx, data.Headers[1:]) return []abci.ValidatorUpdate{} } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 49908851..ebaebc32 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -1,8 +1,6 @@ package keeper import ( - "encoding/hex" - "github.com/summa-tx/relays/golang/x/relay/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,14 +12,7 @@ func (k Keeper) getChainStore(ctx sdk.Context) sdk.KVStore { } func (k Keeper) emitReorg(ctx sdk.Context, prev, new, lca types.BitcoinHeader) { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeReorg, - sdk.NewAttribute(types.AttributeKeyPreviousBest, "0x"+hex.EncodeToString(prev.HashLE[:])), - sdk.NewAttribute(types.AttributeKeyNewBest, "0x"+hex.EncodeToString(new.HashLE[:])), - sdk.NewAttribute(types.AttributeKeyLatestCommon, "0x"+hex.EncodeToString(lca.HashLE[:])), - ), - ) + ctx.EventManager().EmitEvent(types.NewReorgEvent(prev, new, lca)) } // SetGenesisState sets the genesis state diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index e9b12bda..62a3dfdb 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/hex" "math/big" "github.com/summa-tx/bitcoin-spv/golang/btcspv" @@ -15,13 +14,7 @@ func (k Keeper) getHeaderStore(ctx sdk.Context) sdk.KVStore { } func (k Keeper) emitExtension(ctx sdk.Context, first, last types.BitcoinHeader) { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeExtension, - sdk.NewAttribute(types.AttributeKeyFirstBlock, "0x"+hex.EncodeToString(first.HashLE[:])), - sdk.NewAttribute(types.AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.HashLE[:])), - ), - ) + ctx.EventManager().EmitEvent(types.NewExtensionEvent(first, last)) } // HasHeader checks if a header is in the store diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 1cad51d3..83906fb0 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -1,5 +1,11 @@ package types +import ( + "encoding/hex" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + // Relay module event types const ( EventTypeExtension = "extension" @@ -13,5 +19,21 @@ const ( AttributeKeyLatestCommon = "latest_common_ancestor" ) -// event Extension(bytes32 indexed _first, bytes32 indexed _last); -// event Reorg(bytes32 indexed _from, bytes32 indexed _to, bytes32 indexed _gcd); +// NewReorgEvent instantiates a reorg event +func NewReorgEvent(prev, new, lca BitcoinHeader) sdk.Event { + return sdk.NewEvent( + EventTypeReorg, + sdk.NewAttribute(AttributeKeyPreviousBest, "0x"+hex.EncodeToString(prev.HashLE[:])), + sdk.NewAttribute(AttributeKeyNewBest, "0x"+hex.EncodeToString(new.HashLE[:])), + sdk.NewAttribute(AttributeKeyLatestCommon, "0x"+hex.EncodeToString(lca.HashLE[:])), + ) +} + +// NewExtensionEvent instantiates an extension event +func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { + return sdk.NewEvent( + EventTypeExtension, + sdk.NewAttribute(AttributeKeyFirstBlock, "0x"+hex.EncodeToString(first.HashLE[:])), + sdk.NewAttribute(AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.HashLE[:])), + ) +} From e42938665c18e0692fd81da4d803d86a7d972e26 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 19 Nov 2019 14:19:29 -0800 Subject: [PATCH 020/148] feature: testnet mode scaffolding --- golang/README.md | 2 ++ golang/app.go | 7 +++---- golang/x/relay/keeper/headers.go | 4 ++-- golang/x/relay/keeper/keeper.go | 12 +++++++----- golang/x/relay/module.go | 5 +---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/golang/README.md b/golang/README.md index bb7833cf..151cf661 100644 --- a/golang/README.md +++ b/golang/README.md @@ -4,6 +4,8 @@ 1. Add iterator for links 1. Add reasonable genesis state 1. Add state export +1. Pass in testnet or mainnet status + 1. Maybe genesis state? that's not perfect tho ## How to add a view function 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` diff --git a/golang/app.go b/golang/app.go index df241ff4..8e36e90d 100644 --- a/golang/app.go +++ b/golang/app.go @@ -93,9 +93,7 @@ func MakeCodec() *codec.Codec { } // NewRelayApp instantiates a new Bitcoin relay app -func NewRelayApp( - logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp), -) *relayApp { +func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *relayApp { // First define the top level codec that will be shared by the different modules. Note: Codec will be explained later cdc := MakeCodec() @@ -197,6 +195,7 @@ func NewRelayApp( app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], app.cdc, + true, // TODO: pass this in somehow ) app.mm = module.NewManager( @@ -204,7 +203,7 @@ func NewRelayApp( genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx), auth.NewAppModule(app.accountKeeper), bank.NewAppModule(app.bankKeeper, app.accountKeeper), - relay.NewAppModule(app.relayKeeper, app.bankKeeper), + relay.NewAppModule(app.relayKeeper), supply.NewAppModule(app.supplyKeeper, app.accountKeeper), distr.NewAppModule(app.distrKeeper, app.supplyKeeper), slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 62a3dfdb..f8ed13f5 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -75,7 +75,6 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in copy(raw[0:80], anchor.Raw[:]) // Make the raw chain - // TODO: test behavior for i, header := range headers { _, err := header.Validate() if err != nil { @@ -88,7 +87,8 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in } // ensure expectedTarget doesn't change - if btcspv.ExtractTarget(header.Raw) != expectedTarget { + // it's allowed to change if the relay is in testnet mode + if k.IsMainNet && btcspv.ExtractTarget(header.Raw) != expectedTarget { return types.ErrUnexptectedRetarget(types.DefaultCodespace) } diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index ba3e2396..c15f5596 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -8,15 +8,17 @@ import ( // Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine type Keeper struct { - storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context - cdc *codec.Codec // The wire codec for binary encoding/decoding. + storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context + cdc *codec.Codec // The wire codec for binary encoding/decoding. + IsMainNet bool } // NewKeeper instantiates a new keeper -func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec) Keeper { +func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec, mainnet bool) Keeper { return Keeper{ - storeKey: storeKey, - cdc: cdc, + storeKey: storeKey, + cdc: cdc, + IsMainNet: mainnet, } } diff --git a/golang/x/relay/module.go b/golang/x/relay/module.go index 3d6826c3..2de6388f 100644 --- a/golang/x/relay/module.go +++ b/golang/x/relay/module.go @@ -8,7 +8,6 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/module" - "github.com/cosmos/cosmos-sdk/x/bank" "github.com/summa-tx/relays/golang/x/relay/client/cli" "github.com/summa-tx/relays/golang/x/relay/client/rest" @@ -73,15 +72,13 @@ func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic keeper Keeper - coinKeeper bank.Keeper } // NewAppModule creates a new AppModule Object -func NewAppModule(k Keeper, bankKeeper bank.Keeper) AppModule { +func NewAppModule(k Keeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: k, - coinKeeper: bankKeeper, } } From a2dafe4486d7b9426872beb09d3839fadaa779a4 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 19 Nov 2019 14:41:07 -0800 Subject: [PATCH 021/148] feature: LCA getter and setter --- golang/x/relay/keeper/chain.go | 41 +++++++++++++++++++++++++--------- golang/x/relay/types/keys.go | 3 +++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index ebaebc32..6446d90b 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -33,6 +33,13 @@ func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.Bitc } +// setRelayGenesis sets the first digest in the relay +func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := k.getChainStore(ctx) + store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) +} + // GetRelayGenesis returns the first digest in the relay func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { store := k.getChainStore(ctx) @@ -40,19 +47,12 @@ func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error digest, err := btcspv.NewHash256Digest(result) if err != nil { - return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) } return digest, nil } -// setRelayGenesis sets the first digest in the relay -func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state - store := k.getChainStore(ctx) - store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) -} - -// setBestKnownDigest sets the first digest in the relay +// setBestKnownDigest sets the best known chain tip func (k Keeper) setBestKnownDigest(ctx sdk.Context, bestKnown types.Hash256Digest) { // TODO: Remove this in favor of Genesis state store := k.getChainStore(ctx) @@ -60,13 +60,32 @@ func (k Keeper) setBestKnownDigest(ctx sdk.Context, bestKnown types.Hash256Diges } // GetBestKnownDigest returns the best known digest in the relay -func (k Keeper) GetBestKnownDigest(ctx sdk.Context, digest types.Hash256Digest) (types.Hash256Digest, sdk.Error) { +func (k Keeper) GetBestKnownDigest(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { store := k.getChainStore(ctx) result := store.Get([]byte(types.BestKnownDigestStorage)) digest, err := btcspv.NewHash256Digest(result) if err != nil { - return digest, types.ErrBadHash256Digest(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil +} + +// setLastReorgLCA sets the latest common ancestor of the last reorg +func (k Keeper) setLastReorgLCA(ctx sdk.Context, bestKnown types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := k.getChainStore(ctx) + store.Set([]byte(types.LastReorgLCAStorage), bestKnown[:]) +} + +// GetLastReorgLCA returns the best known digest in the relay +func (k Keeper) GetLastReorgLCA(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { + store := k.getChainStore(ctx) + result := store.Get([]byte(types.LastReorgLCAStorage)) + + digest, err := btcspv.NewHash256Digest(result) + if err != nil { + return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) } return digest, nil } diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index 579eaf08..be796576 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -21,4 +21,7 @@ const ( // BestKnownDigestStorage is the storage key for the best known digest BestKnownDigestStorage = "BestKnownDigest" + + // LastReorgLCAStorage is the storage key for the last reorg LCA + LastReorgLCAStorage = "LastReorgLCA" ) From f8f5d64b3ae9a6d210a6d1c7b0de9a1fda27478a Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 19 Nov 2019 15:04:21 -0800 Subject: [PATCH 022/148] refactor: break out common blocks from getters/setters --- golang/x/relay/keeper/chain.go | 58 ++++++++++++++-------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 6446d90b..5d300240 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -15,6 +15,23 @@ func (k Keeper) emitReorg(ctx sdk.Context, prev, new, lca types.BitcoinHeader) { ctx.EventManager().EmitEvent(types.NewReorgEvent(prev, new, lca)) } +func (k Keeper) getDigestByStoreKey(ctx sdk.Context, key string) (types.Hash256Digest, sdk.Error) { + store := k.getChainStore(ctx) + result := store.Get([]byte(key)) + + digest, err := btcspv.NewHash256Digest(result) + if err != nil { + return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) + } + return digest, nil +} + +func (k Keeper) setDigestByStoreKey(ctx sdk.Context, key string, digest types.Hash256Digest) { + // TODO: Remove this in favor of Genesis state + store := k.getChainStore(ctx) + store.Set([]byte(key), digest[:]) +} + // SetGenesisState sets the genesis state func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { store := k.getChainStore(ctx) @@ -28,64 +45,37 @@ func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.Bitc k.setRelayGenesis(ctx, genesis.HashLE) k.setBestKnownDigest(ctx, genesis.HashLE) + k.setLastReorgLCA(ctx, genesis.HashLE) return nil - } // setRelayGenesis sets the first digest in the relay func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state - store := k.getChainStore(ctx) - store.Set([]byte(types.RelayGenesisStorage), relayGenesis[:]) + k.setDigestByStoreKey(ctx, types.RelayGenesisStorage, relayGenesis) } // GetRelayGenesis returns the first digest in the relay func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - store := k.getChainStore(ctx) - result := store.Get([]byte(types.RelayGenesisStorage)) - - digest, err := btcspv.NewHash256Digest(result) - if err != nil { - return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil + return k.getDigestByStoreKey(ctx, types.RelayGenesisStorage) } // setBestKnownDigest sets the best known chain tip func (k Keeper) setBestKnownDigest(ctx sdk.Context, bestKnown types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state - store := k.getChainStore(ctx) - store.Set([]byte(types.BestKnownDigestStorage), bestKnown[:]) + k.setDigestByStoreKey(ctx, types.BestKnownDigestStorage, bestKnown) } // GetBestKnownDigest returns the best known digest in the relay func (k Keeper) GetBestKnownDigest(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - store := k.getChainStore(ctx) - result := store.Get([]byte(types.BestKnownDigestStorage)) - - digest, err := btcspv.NewHash256Digest(result) - if err != nil { - return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil + return k.getDigestByStoreKey(ctx, types.BestKnownDigestStorage) } // setLastReorgLCA sets the latest common ancestor of the last reorg func (k Keeper) setLastReorgLCA(ctx sdk.Context, bestKnown types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state - store := k.getChainStore(ctx) - store.Set([]byte(types.LastReorgLCAStorage), bestKnown[:]) + k.setDigestByStoreKey(ctx, types.LastReorgLCAStorage, bestKnown) } // GetLastReorgLCA returns the best known digest in the relay func (k Keeper) GetLastReorgLCA(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - store := k.getChainStore(ctx) - result := store.Get([]byte(types.LastReorgLCAStorage)) - - digest, err := btcspv.NewHash256Digest(result) - if err != nil { - return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) - } - return digest, nil + return k.getDigestByStoreKey(ctx, types.LastReorgLCAStorage) } From 1fa4fcee547ec5d73b99e8114c162bb377463805 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 20 Nov 2019 14:02:57 -0800 Subject: [PATCH 023/148] feature: MarkNewHeaviest --- golang/x/relay/keeper/chain.go | 119 ++++++++++++++++++++++++++++++++- golang/x/relay/types/errors.go | 5 ++ golang/x/relay/types/events.go | 8 +-- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 5d300240..e7f977cf 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -11,7 +11,7 @@ func (k Keeper) getChainStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.ChainStorePrefix) } -func (k Keeper) emitReorg(ctx sdk.Context, prev, new, lca types.BitcoinHeader) { +func (k Keeper) emitReorg(ctx sdk.Context, prev, new, lca types.Hash256Digest) { ctx.EventManager().EmitEvent(types.NewReorgEvent(prev, new, lca)) } @@ -79,3 +79,120 @@ func (k Keeper) setLastReorgLCA(ctx sdk.Context, bestKnown types.Hash256Digest) func (k Keeper) GetLastReorgLCA(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { return k.getDigestByStoreKey(ctx, types.LastReorgLCAStorage) } + +func (k Keeper) isMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, right types.Hash256Digest, limit uint32) bool { + if ancestor == left && ancestor == right { + return true + } + + leftCurrent := left + leftPrev := left + + rightCurrent := right + rightPrev := right + + for i := uint32(0); i < limit; i++ { + if leftPrev != ancestor { + leftCurrent = leftPrev + leftPrev = k.getLink(ctx, leftPrev) + } + if rightPrev != ancestor { + rightCurrent = rightPrev + rightPrev = k.getLink(ctx, rightPrev) + } + if leftPrev == rightPrev { + break + } + } + + if leftCurrent == rightCurrent { + return false + } + + if leftPrev != rightPrev { + return false + } + + return true +} + +func (k Keeper) heaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, newBest types.Hash256Digest, limit uint32) (types.Hash256Digest, sdk.Error) { + ancestorBlock := k.GetHeader(ctx, ancestor) + leftBlock := k.GetHeader(ctx, currentBest) + rightBlock := k.GetHeader(ctx, newBest) + + if leftBlock.Height < ancestorBlock.Height || rightBlock.Height < ancestorBlock.Height { + return types.Hash256Digest{}, types.ErrBadHeight(types.DefaultCodespace) + } + + nextPeriodStartHeight := ancestorBlock.Height + 2016 - (ancestorBlock.Height % 2016) + leftInPeriod := leftBlock.Height < nextPeriodStartHeight + rightInPeriod := rightBlock.Height < nextPeriodStartHeight + + /* + NB: + 1. Left is in a new window, right is in the old window. Left is heavier + 2. Right is in a new window, left is in the old window. Right is heavier + 3. Both are in the same window, choose the higher one + 4. They're in different new windows. Choose the heavier one + */ + + if !leftInPeriod && rightInPeriod { + return leftBlock.HashLE, nil + } + if leftInPeriod && !rightInPeriod { + return rightBlock.HashLE, nil + } + if leftInPeriod && rightInPeriod { + if leftBlock.Height >= rightBlock.Height { + return leftBlock.HashLE, nil + } + return rightBlock.HashLE, nil + } + + // if !leftInPeriod && !rightInPeriod + leftDiff := btcspv.ExtractDifficulty(leftBlock.Raw) + leftAccDiff := leftDiff.Mul(sdk.NewUint(uint64(leftBlock.Height % 2016))) + + rightDiff := btcspv.ExtractDifficulty(rightBlock.Raw) + rightAccDiff := rightDiff.Mul(sdk.NewUint(uint64(rightBlock.Height % 2016))) + + if leftAccDiff.GTE(rightAccDiff) { + return leftBlock.HashLE, nil + } + return rightBlock.HashLE, nil +} + +// MarkNewHeaviest updates the best known digest and LCA +func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, currentBest, newBest types.RawHeader, limit uint32) sdk.Error { + newBestDigest := btcspv.Hash256(newBest[:]) + currentBestDigest := btcspv.Hash256(currentBest[:]) + + knownBestDigest, err := k.GetBestKnownDigest(ctx) + if err != nil || currentBestDigest != knownBestDigest { + return types.ErrNotBestKnown(types.DefaultCodespace) + } + + if !k.HasHeader(ctx, newBestDigest) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + + if !k.isMostRecentCommonAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) { + return types.ErrNotHeaviestAncestor(types.DefaultCodespace) + } + + better, err := k.heaviestFromAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) + if err != nil { + return err + } + + if newBestDigest != better { + return types.ErrNotHeavier(types.DefaultCodespace) + } + + k.setLastReorgLCA(ctx, ancestor) + k.setBestKnownDigest(ctx, newBestDigest) + k.emitReorg(ctx, knownBestDigest, newBestDigest, ancestor) + + return nil +} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 92c3ab44..31a6bb80 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -71,6 +71,11 @@ const ( NotHeavier sdk.CodeType = 405 ) +// ErrUnknownError throws an error +func ErrUnknownError(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, UnknownError, "Unknown Error") +} + // ErrBadHeaderLength throws an error func ErrBadHeaderLength(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, BadHeaderLength, "Header array length must be divisible by 80") diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 83906fb0..bdf3670c 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -20,12 +20,12 @@ const ( ) // NewReorgEvent instantiates a reorg event -func NewReorgEvent(prev, new, lca BitcoinHeader) sdk.Event { +func NewReorgEvent(prev, new, lca Hash256Digest) sdk.Event { return sdk.NewEvent( EventTypeReorg, - sdk.NewAttribute(AttributeKeyPreviousBest, "0x"+hex.EncodeToString(prev.HashLE[:])), - sdk.NewAttribute(AttributeKeyNewBest, "0x"+hex.EncodeToString(new.HashLE[:])), - sdk.NewAttribute(AttributeKeyLatestCommon, "0x"+hex.EncodeToString(lca.HashLE[:])), + sdk.NewAttribute(AttributeKeyPreviousBest, "0x"+hex.EncodeToString(prev[:])), + sdk.NewAttribute(AttributeKeyNewBest, "0x"+hex.EncodeToString(new[:])), + sdk.NewAttribute(AttributeKeyLatestCommon, "0x"+hex.EncodeToString(lca[:])), ) } From 081d5ae62cd9e2580a024c6c94631dd02eb36fe2 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 20 Nov 2019 14:44:06 -0800 Subject: [PATCH 024/148] feature: IsAncestor and FindAncestor --- golang/x/relay/keeper/links.go | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index 11dc0d14..a036a65e 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -11,6 +11,11 @@ func (k Keeper) getLinkStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.LinkStorePrefix) } +func (k Keeper) hasLink(ctx sdk.Context, digestLE types.Hash256Digest) bool { + store := k.getLinkStore(ctx) + return store.Has(digestLE[:]) +} + func (k Keeper) setLink(ctx sdk.Context, header types.BitcoinHeader) { store := k.getLinkStore(ctx) store.Set(header.HashLE[:], header.PrevHashLE[:]) @@ -23,3 +28,36 @@ func (k Keeper) getLink(ctx sdk.Context, digestLE types.Hash256Digest) types.Has parentHashLE, _ := btcspv.NewHash256Digest(buf) return parentHashLE } + +// FindAncestor finds the nth ancestor of some digest +func (k Keeper) FindAncestor(ctx sdk.Context, digestLE types.Hash256Digest, offset uint32) (types.Hash256Digest, sdk.Error) { + current := digestLE + if !k.hasLink(ctx, current) { + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + + for i := uint32(0); i < offset; i++ { + current := k.getLink(ctx, current) + if !k.hasLink(ctx, current) { + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + } + + return current, nil +} + +// IsAncestor checks whether +func (k Keeper) IsAncestor(ctx sdk.Context, ancestor, digestLE types.Hash256Digest, limit uint32) bool { + current := digestLE + + for i := uint32(0); i < limit; i++ { + if !k.hasLink(ctx, current) { + return false + } + current := k.getLink(ctx, current) + if current == ancestor { + return true + } + } + return false +} From 55f17269886aa8c65ad7bb4590d71e512f0ce9cf Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 20 Nov 2019 14:49:41 -0800 Subject: [PATCH 025/148] feature: make private functions public in chain.go --- golang/x/relay/keeper/chain.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index e7f977cf..4cf97417 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -80,7 +80,8 @@ func (k Keeper) GetLastReorgLCA(ctx sdk.Context) (types.Hash256Digest, sdk.Error return k.getDigestByStoreKey(ctx, types.LastReorgLCAStorage) } -func (k Keeper) isMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, right types.Hash256Digest, limit uint32) bool { +// IsMostRecentCommonAncestor checks if a proposed ancestor is the LCA of two digests +func (k Keeper) IsMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, right types.Hash256Digest, limit uint32) bool { if ancestor == left && ancestor == right { return true } @@ -116,7 +117,8 @@ func (k Keeper) isMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, righ return true } -func (k Keeper) heaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, newBest types.Hash256Digest, limit uint32) (types.Hash256Digest, sdk.Error) { +// HeaviestFromAncestor determines the heavier descendant of a common ancestor +func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, newBest types.Hash256Digest, limit uint32) (types.Hash256Digest, sdk.Error) { ancestorBlock := k.GetHeader(ctx, ancestor) leftBlock := k.GetHeader(ctx, currentBest) rightBlock := k.GetHeader(ctx, newBest) @@ -177,11 +179,11 @@ func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, c return types.ErrUnknownBlock(types.DefaultCodespace) } - if !k.isMostRecentCommonAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) { + if !k.IsMostRecentCommonAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) { return types.ErrNotHeaviestAncestor(types.DefaultCodespace) } - better, err := k.heaviestFromAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) + better, err := k.HeaviestFromAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) if err != nil { return err } From 7e77730f57b4285531067f24f172e26c090329a2 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Tue, 26 Nov 2019 15:12:58 -0700 Subject: [PATCH 026/148] made function skeletons --- golang/x/relay/types/msgs.go | 140 +++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 10861696..a5effb5e 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -51,3 +51,143 @@ func (msg MsgSetLink) GetSignBytes() []byte { // Route should return the name of the module func (msg MsgSetLink) Route() string { return RouterKey } + +// TODO: Write AddHeaders +// AddHeaders adds headers to storage after validating. We check +// integrity and consistency of the header chain. +func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { + +} + +// function _addHeaders(bytes memory _anchor, bytes memory _headers, bool _internal) internal returns (bool) { +// uint256 _height; +// bytes memory _header; +// bytes32 _currentDigest; +// bytes32 _previousDigest = _anchor.hash256(); + +// uint256 _target = _headers.slice(0, 80).extractTarget(); +// uint256 _anchorHeight = _findHeight(_previousDigest); /* NB: errors if unknown */ + +// require( +// _internal || _anchor.extractTarget() == _target, +// "Unexpected retarget on external call"); +// require(_headers.length % 80 == 0, "Header array length must be divisible by 80"); + +// /* +// NB: +// 1. check that the header has sufficient work +// 2. check that headers are in a coherent chain (no retargets, hash links good) +// 3. Store the block connection +// 4. Store the height +// */ +// for (uint256 i = 0; i < _headers.length / 80; i = i.add(1)) { +// _header = _headers.slice(i.mul(80), 80); +// _height = _anchorHeight.add(i + 1); +// _currentDigest = _header.hash256(); + +// /* +// NB: +// if the block is already authenticated, we don't need to a work check +// Or write anything to state. This saves gas +// */ +// if (previousBlock[_currentDigest] == bytes32(0)) { +// require( +// abi.encodePacked(_currentDigest).reverseEndianness().bytesToUint() <= _target, +// "Header work is insufficient"); +// previousBlock[_currentDigest] = _previousDigest; +// if (_height % HEIGHT_INTERVAL == 0) { +// /* +// NB: We store the height only every 4th header to save gas +// */ +// blockHeight[_currentDigest] = _height; +// } +// } + +// /* NB: we do still need to make chain level checks tho */ +// require(_header.extractTarget() == _target, "Target changed unexpectedly"); +// require(_header.validateHeaderPrevHash(_previousDigest), "Headers do not form a consistent chain"); + +// _previousDigest = _currentDigest; +// } + +// emit Extension( +// _anchor.hash256(), +// _currentDigest); +// return true; +// } + +// TODO: write AddHeadersWithRetarget +// Adds headers to storage, performs additional validation of retarget. +func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []byte, headers []byte) (bool, error) { + +} + +// function _addHeadersWithRetarget( +// bytes memory _oldPeriodStartHeader, +// bytes memory _oldPeriodEndHeader, +// bytes memory _headers +// ) internal returns (bool) { +// /* NB: requires that both blocks are known */ +// uint256 _startHeight = _findHeight(_oldPeriodStartHeader.hash256()); +// uint256 _endHeight = _findHeight(_oldPeriodEndHeader.hash256()); + +// /* NB: retargets should happen at 2016 block intervals */ +// require( +// _endHeight % 2016 == 2015, +// "Must provide the last header of the closing difficulty period"); +// require( +// _endHeight == _startHeight.add(2015), +// "Must provide exactly 1 difficulty period"); +// require( +// _oldPeriodStartHeader.extractDifficulty() == _oldPeriodEndHeader.extractDifficulty(), +// "Period header difficulties do not match"); + +// /* NB: This comparison looks weird because header nBits encoding truncates targets */ +// bytes memory _newPeriodStart = _headers.slice(0, 80); +// uint256 _actualTarget = _newPeriodStart.extractTarget(); +// uint256 _expectedTarget = BTCUtils.retargetAlgorithm( +// _oldPeriodStartHeader.extractTarget(), +// _oldPeriodStartHeader.extractTimestamp(), +// _oldPeriodEndHeader.extractTimestamp() +// ); +// require( +// (_actualTarget & _expectedTarget) == _actualTarget, +// "Invalid retarget provided"); + +// // Pass all but the first through to be added +// return _addHeaders(_oldPeriodEndHeader, _headers, true); +// } + +// TODO: write MarkNewHeaviest +// Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis +func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit uint256) (bool, error) { + +} + +// function _markNewHeaviest( +// bytes32 _ancestor, +// bytes memory _currentBest, +// bytes memory _newBest, +// uint256 _limit +// ) internal returns (bool) { +// bytes32 _newBestDigest = _newBest.hash256(); +// bytes32 _currentBestDigest = _currentBest.hash256(); +// require(_currentBestDigest == bestKnownDigest, "Passed in best is not best known"); +// require( +// previousBlock[_newBestDigest] != bytes32(0), +// "New best is unknown"); +// require( +// _isMostRecentAncestor(_ancestor, bestKnownDigest, _newBestDigest, _limit), +// "Ancestor must be heaviest common ancestor"); +// require( +// _heaviestFromAncestor(_ancestor, _currentBest, _newBest) == _newBestDigest, +// "New best hash does not have more work than previous"); + +// bestKnownDigest = _newBestDigest; +// lastReorgCommonAncestor = _ancestor; +// emit Reorg( +// _currentBestDigest, +// _newBestDigest, +// _ancestor); +// return true; +// } From d2b9f4b8e966a568ad301ea85f2e6004a0c9b235 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Tue, 26 Nov 2019 16:25:42 -0700 Subject: [PATCH 027/148] mostly wrote functions --- golang/x/relay/types/msgs.go | 243 +++++++++++++++++------------------ 1 file changed, 118 insertions(+), 125 deletions(-) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index a5effb5e..af532e1d 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -1,14 +1,22 @@ package types import ( + "bytes" "encoding/hex" + "errors" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // RouterKey is a name for the router const RouterKey = ModuleName // this was defined in your key.go file +const HEIGHT_INTERVAL = 4 + +var bestKnownDigest []byte +var lastReorgCommonAncestor []byte + // MsgSetLink defines a SetLink message type MsgSetLink struct { Address string `json:"signer"` @@ -52,142 +60,127 @@ func (msg MsgSetLink) GetSignBytes() []byte { // Route should return the name of the module func (msg MsgSetLink) Route() string { return RouterKey } -// TODO: Write AddHeaders +// TODO: Add digest type that is 32 bytes long // AddHeaders adds headers to storage after validating. We check // integrity and consistency of the header chain. func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { + var height sdk.Uint + var header []byte + var currentDigest []byte + var previousDigest []byte = btcspv.Hash256(anchor) + + target := btcspv.ExtractTarget(headers[0:80]) + // TODO: write findHeight in queries + anchorHeight := findHeight(previousDigest) /* NB: errors if unknown */ + extractedTarget := btcspv.ExtractTarget(anchor) + if !(internal || extractedTarget == target) { + return false, errors.New("Header array length must be divisible by 80") + } + /* + NB: + 1. check that the header has sufficient work + 2. check that headers are in a coherent chain (no retargets, hash links good) + 3. Store the block connection + 4. Store the height + */ + for i := 0; i < len(headers)/80; i++ { + start := i * 80 + header := headers[start:(start + 80)] + height := anchorHeight + i + 1 + currentDigest = btcspv.Hash256(header) + /* + NB: + if the block is already authenticated, we don't need to a work check + Or write anything to state. This saves gas + */ + if bytes.Equal(currentDigest, bytes.Repeat([]byte{0}, 32)) { + if btcspv.BytesToBigUint(btcspv.ReverseEndianness(currentDigest)).LTE(target) { + return false, errors.New("Header work is insufficient") + } + currentDigest = previousDigest + if height%HEIGHT_INTERVAL == 0 { + /* + NB: We store the height only every 4th header to save gas + */ + currentDigest = height + } + } + /* NB: we do still need to make chain level checks tho */ + if !(btcspv.ExtractTarget(header) == target) { + return false, errors.New("Target changed unexpectedly") + } + if !(btcspv.ValidateHeaderPrevHash(header, previousDigest)) { + return false, errors.New("Headers do not form a consistent chain") + } + + previousDigest = currentDigest + } + // TODO: How to do this in go... + // emit Extension( + // _anchor.hash256(), + // _currentDigest); + return true, nil } -// function _addHeaders(bytes memory _anchor, bytes memory _headers, bool _internal) internal returns (bool) { -// uint256 _height; -// bytes memory _header; -// bytes32 _currentDigest; -// bytes32 _previousDigest = _anchor.hash256(); - -// uint256 _target = _headers.slice(0, 80).extractTarget(); -// uint256 _anchorHeight = _findHeight(_previousDigest); /* NB: errors if unknown */ - -// require( -// _internal || _anchor.extractTarget() == _target, -// "Unexpected retarget on external call"); -// require(_headers.length % 80 == 0, "Header array length must be divisible by 80"); - -// /* -// NB: -// 1. check that the header has sufficient work -// 2. check that headers are in a coherent chain (no retargets, hash links good) -// 3. Store the block connection -// 4. Store the height -// */ -// for (uint256 i = 0; i < _headers.length / 80; i = i.add(1)) { -// _header = _headers.slice(i.mul(80), 80); -// _height = _anchorHeight.add(i + 1); -// _currentDigest = _header.hash256(); - -// /* -// NB: -// if the block is already authenticated, we don't need to a work check -// Or write anything to state. This saves gas -// */ -// if (previousBlock[_currentDigest] == bytes32(0)) { -// require( -// abi.encodePacked(_currentDigest).reverseEndianness().bytesToUint() <= _target, -// "Header work is insufficient"); -// previousBlock[_currentDigest] = _previousDigest; -// if (_height % HEIGHT_INTERVAL == 0) { -// /* -// NB: We store the height only every 4th header to save gas -// */ -// blockHeight[_currentDigest] = _height; -// } -// } - -// /* NB: we do still need to make chain level checks tho */ -// require(_header.extractTarget() == _target, "Target changed unexpectedly"); -// require(_header.validateHeaderPrevHash(_previousDigest), "Headers do not form a consistent chain"); - -// _previousDigest = _currentDigest; -// } - -// emit Extension( -// _anchor.hash256(), -// _currentDigest); -// return true; -// } - -// TODO: write AddHeadersWithRetarget // Adds headers to storage, performs additional validation of retarget. func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []byte, headers []byte) (bool, error) { + /* NB: requires that both blocks are known */ + startHeight := findHeight(btcspv.Hash256(oldPeriodStartHeader)) + endHeight := findHeight(btcspv.Hash256(oldPeriodEndHeader)) + + /* NB: retargets should happen at 2016 block intervals */ + if endHeight%2016 != 2015 { + return false, errors.New("Must provide the last header of the closing difficulty period") + } else if endHeight != startHeight+2015 { + return false, errors.New("Must provide exactly 1 difficulty period") + } else if btcspv.ExtractDifficulty(oldPeriodStartHeader) != btcspv.ExtractDifficulty(oldPeriodEndHeader) { + return false, errors.New("Period header difficulties do not match") + } -} + /* NB: This comparison looks weird because header nBits encoding truncates targets */ + + newPeriodStart := headers[0:80] + actualTarget := btcspv.ExtractTarget(newPeriodStart) + expectedTarget := btcspv.RetargetAlgorithm( + btcspv.ExtractTarget(oldPeriodStartHeader), + btcspv.ExtractTimestamp(oldPeriodStartHeader), + btcspv.ExtractTimestamp(oldPeriodEndHeader)) + // TODO: Fix next line & + if actualTarget&expectedTarget != actualTarget { + return false, errors.New("Invalid retarget provided") + } + + // Pass all but the first through to be added + return AddHeaders(oldPeriodEndHeader, headers, true) -// function _addHeadersWithRetarget( -// bytes memory _oldPeriodStartHeader, -// bytes memory _oldPeriodEndHeader, -// bytes memory _headers -// ) internal returns (bool) { -// /* NB: requires that both blocks are known */ -// uint256 _startHeight = _findHeight(_oldPeriodStartHeader.hash256()); -// uint256 _endHeight = _findHeight(_oldPeriodEndHeader.hash256()); - -// /* NB: retargets should happen at 2016 block intervals */ -// require( -// _endHeight % 2016 == 2015, -// "Must provide the last header of the closing difficulty period"); -// require( -// _endHeight == _startHeight.add(2015), -// "Must provide exactly 1 difficulty period"); -// require( -// _oldPeriodStartHeader.extractDifficulty() == _oldPeriodEndHeader.extractDifficulty(), -// "Period header difficulties do not match"); - -// /* NB: This comparison looks weird because header nBits encoding truncates targets */ -// bytes memory _newPeriodStart = _headers.slice(0, 80); -// uint256 _actualTarget = _newPeriodStart.extractTarget(); -// uint256 _expectedTarget = BTCUtils.retargetAlgorithm( -// _oldPeriodStartHeader.extractTarget(), -// _oldPeriodStartHeader.extractTimestamp(), -// _oldPeriodEndHeader.extractTimestamp() -// ); -// require( -// (_actualTarget & _expectedTarget) == _actualTarget, -// "Invalid retarget provided"); - -// // Pass all but the first through to be added -// return _addHeaders(_oldPeriodEndHeader, _headers, true); -// } + return true, nil +} // TODO: write MarkNewHeaviest // Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis -func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit uint256) (bool, error) { - +func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit sdk.Uint) (bool, error) { + newBestDigest := btcspv.Hash256(newBest) + currentBestDigest := btcspv.Hash256(currentBest) + // TODO: Where is bestKnownDigest coming from? + if !bytes.Equal(currentBestDigest, bestKnownDigest) { + return false, errors.New("Passed in best is not best known") + } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { + return false, errors.New("New best is unknown") + } else if !isMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { + return false, errors.New("Ancestor must be heaviest common ancestor") + } else if !bytes.Equal( + heaviestFromAncestor(ancestor, currentBest, newBest), + newBestDigest) { + return false, errors.New("New best hash does not have more work than previous") + } + bestKnownDigest = newBestDigest + lastReorgCommonAncestor = ancestor + // TODO: Figure out how to do this in go + // emit Reorg( + // _currentBestDigest, + // _newBestDigest, + // _ancestor); + + return true, nil } - -// function _markNewHeaviest( -// bytes32 _ancestor, -// bytes memory _currentBest, -// bytes memory _newBest, -// uint256 _limit -// ) internal returns (bool) { -// bytes32 _newBestDigest = _newBest.hash256(); -// bytes32 _currentBestDigest = _currentBest.hash256(); -// require(_currentBestDigest == bestKnownDigest, "Passed in best is not best known"); -// require( -// previousBlock[_newBestDigest] != bytes32(0), -// "New best is unknown"); -// require( -// _isMostRecentAncestor(_ancestor, bestKnownDigest, _newBestDigest, _limit), -// "Ancestor must be heaviest common ancestor"); -// require( -// _heaviestFromAncestor(_ancestor, _currentBest, _newBest) == _newBestDigest, -// "New best hash does not have more work than previous"); - -// bestKnownDigest = _newBestDigest; -// lastReorgCommonAncestor = _ancestor; -// emit Reorg( -// _currentBestDigest, -// _newBestDigest, -// _ancestor); -// return true; -// } From 27d0566aac38a73da48b9f44088dc6a7c175d8f5 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 10:18:26 -0700 Subject: [PATCH 028/148] Added function skeletons in querier.go --- golang/x/relay/types/msgs.go | 12 +-- golang/x/relay/types/querier.go | 163 +++++++++++++++++++++++++++++++- solidity/contracts/Relay.sol | 2 +- 3 files changed, 169 insertions(+), 8 deletions(-) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index af532e1d..33e1c67d 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -118,8 +118,8 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { } // TODO: How to do this in go... // emit Extension( - // _anchor.hash256(), - // _currentDigest); + // btcspv.Hash256(anchor), + // currentDigest); return true, nil } @@ -157,7 +157,7 @@ func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []by return true, nil } -// TODO: write MarkNewHeaviest +// TODO: write isMostRecentAncestor and heaviestFromAncestor // Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit sdk.Uint) (bool, error) { newBestDigest := btcspv.Hash256(newBest) @@ -178,9 +178,9 @@ func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit lastReorgCommonAncestor = ancestor // TODO: Figure out how to do this in go // emit Reorg( - // _currentBestDigest, - // _newBestDigest, - // _ancestor); + // currentBestDigest, + // newBestDigest, + // ancestor); return true, nil } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index f548295b..cad95636 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -1,6 +1,11 @@ package types -import "strings" +import ( + "bytes" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) // QueryResGetParent is the payload for a GetParent Query type QueryResGetParent struct { @@ -12,3 +17,159 @@ type QueryResGetParent struct { func (r QueryResGetParent) String() string { return strings.Join([]string{r.Digest, r.Parent}, "\n") } + +var relayGenesis []byte + +// Checks if a digest is an ancestor of the current one +// Limit the amount of lookups (and thus gas usage) with limit +func IsMostRecentAncestor(ancestor []byte, left []byte, right []byte, limit sdk.Uint) bool { + if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { + return true + } + return true +} + +// function _isMostRecentAncestor( +// bytes32 _ancestor, +// bytes32 _left, +// bytes32 _right, +// uint256 _limit +// ) internal view returns (bool) { +// /* NB: sure why not */ +// if (_ancestor == _left && _ancestor == _right) { +// return true; +// } + +// bytes32 _leftCurrent = _left; +// bytes32 _rightCurrent = _right; +// bytes32 _leftPrev = _left; +// bytes32 _rightPrev = _right; + +// for(uint256 i = 0; i < _limit; i = i.add(1)) { +// if (_leftPrev != _ancestor) { +// _leftCurrent = _leftPrev; // cheap +// _leftPrev = previousBlock[_leftPrev]; // expensive +// } +// if (_rightPrev != _ancestor) { +// _rightCurrent = _rightPrev; // cheap +// _rightPrev = previousBlock[_rightPrev]; // expensive +// } +// } +// if (_leftCurrent == _rightCurrent) {return false;} /* NB: If the same, they're a nearer ancestor */ +// if (_leftPrev != _rightPrev) {return false;} /* NB: Both must be ancestor */ +// return true; +// } + +// Getter for relayGenesis. +// This is an initialization parameter. +func GetRelayGenesis() []byte { + return relayGenesis +} + +// function getRelayGenesis() public view returns (bytes32) { +// return relayGenesis; +// } + +// Getter for relayGenesis. +// This is updated only by calling MarkNewHeaviest +func getLastReorgCommonAncestor() []byte { + return lastReorgCommonAncestor +} + +// Finds the height of a header by its digest +// Will fail if the header is unknown +func FindHeight(digest []byte) sdk.Uint { + +} + +// function _findHeight(bytes32 _digest) internal view returns (uint256) { +// uint256 _height = 0; +// bytes32 _current = _digest; +// for (uint256 i = 0; i < HEIGHT_INTERVAL + 1; i = i.add(1)) { +// _height = blockHeight[_current]; +// if (_height == 0) { +// _current = previousBlock[_current]; +// } else { +// return _height.add(i); +// } +// } +// revert("Unknown block"); +// } + +// Finds an ancestor for a block by its digest +// Will fail if the header is unknown +func FindAncestor(digest []byte, offset sdk.Uint) []byte { + +} + +// function _findAncestor(bytes32 _digest, uint256 _offset) internal view returns (bytes32) { +// bytes32 _current = _digest; +// for (uint256 i = 0; i < _offset; i = i.add(1)) { +// _current = previousBlock[_current]; +// } +// require(_current != bytes32(0), "Unknown ancestor"); +// return _current; +// } + +// Checks if a digest is an ancestor of the current one +// Limit the amount of lookups (and thus gas usage) with limit +func IsAncestor(ancestor []byte, descendant []byte, limit sdk.Uint) bool { + return false +} + +// function _isAncestor(bytes32 _ancestor, bytes32 _descendant, uint256 _limit) internal view returns (bool) { +// bytes32 _current = _descendant; +// /* NB: 200 gas/read, so gas is capped at ~200 * limit */ +// for (uint256 i = 0; i < _limit; i = i.add(1)) { +// if (_current == _ancestor) { +// return true; +// } +// _current = previousBlock[_current]; +// } +// return false; +// } + +// Decides which header is heaviest from the ancestor +// Does not support reorgs above 2017 blocks (: +func HeaviestFromAncestor(ancestor []byte, left []byte, right []byte) []byte { + +} + +// function _heaviestFromAncestor( +// bytes32 _ancestor, +// bytes memory _left, +// bytes memory _right +// ) internal view returns (bytes32) { +// uint256 _ancestorHeight = _findHeight(_ancestor); +// uint256 _leftHeight = _findHeight(_left.hash256()); +// uint256 _rightHeight = _findHeight(_right.hash256()); + +// require( +// _leftHeight >= _ancestorHeight && _rightHeight >= _ancestorHeight, +// "A descendant height is below the ancestor height"); + +// /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ +// uint256 _nextPeriodStartHeight = _ancestorHeight.add(2016).sub(_ancestorHeight % 2016); +// bool _leftInPeriod = _leftHeight < _nextPeriodStartHeight; +// bool _rightInPeriod = _rightHeight < _nextPeriodStartHeight; + +// /* +// NB: +// 1. Left is in a new window, right is in the old window. Left is heavier +// 2. Right is in a new window, left is in the old window. Right is heavier +// 3. Both are in the same window, choose the higher one +// 4. They're in different new windows. Choose the heavier one +// */ +// if (!_leftInPeriod && _rightInPeriod) {return _left.hash256();} +// if (_leftInPeriod && !_rightInPeriod) {return _right.hash256();} +// if (_leftInPeriod && _rightInPeriod) { +// return _leftHeight >= _rightHeight ? _left.hash256() : _right.hash256(); +// } else { // if (!_leftInPeriod && !_rightInPeriod) { +// if (((_leftHeight % 2016).mul(_left.extractDifficulty())) < +// (_rightHeight % 2016).mul(_right.extractDifficulty())) { +// return _right.hash256(); +// } else { +// return _left.hash256(); +// } +// } +// } diff --git a/solidity/contracts/Relay.sol b/solidity/contracts/Relay.sol index 3ea4f774..82da0aef 100644 --- a/solidity/contracts/Relay.sol +++ b/solidity/contracts/Relay.sol @@ -457,7 +457,7 @@ contract Relay is IRelay { } /// @notice Getter for relayGenesis - /// @dev This is an initialization paramter + /// @dev This is an initialization parameter /// @return The hash of the first block of the relay function getRelayGenesis() public view returns (bytes32) { return relayGenesis; From b501c5d3c88b251861389e9a7f053179c51301d5 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 11:22:17 -0700 Subject: [PATCH 029/148] mostly wrote querier functions, need to fix errors --- golang/x/relay/types/msgs.go | 14 +-- golang/x/relay/types/querier.go | 209 +++++++++++++++----------------- 2 files changed, 103 insertions(+), 120 deletions(-) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 33e1c67d..ea692a50 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -71,7 +71,7 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { target := btcspv.ExtractTarget(headers[0:80]) // TODO: write findHeight in queries - anchorHeight := findHeight(previousDigest) /* NB: errors if unknown */ + anchorHeight := FindHeight(previousDigest) /* NB: errors if unknown */ extractedTarget := btcspv.ExtractTarget(anchor) if !(internal || extractedTarget == target) { return false, errors.New("Header array length must be divisible by 80") @@ -87,7 +87,7 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { for i := 0; i < len(headers)/80; i++ { start := i * 80 header := headers[start:(start + 80)] - height := anchorHeight + i + 1 + height := anchorHeight.Add(sdk.NewUint(uint64(i + 1))) currentDigest = btcspv.Hash256(header) /* NB: @@ -126,13 +126,13 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { // Adds headers to storage, performs additional validation of retarget. func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []byte, headers []byte) (bool, error) { /* NB: requires that both blocks are known */ - startHeight := findHeight(btcspv.Hash256(oldPeriodStartHeader)) - endHeight := findHeight(btcspv.Hash256(oldPeriodEndHeader)) + startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader)) + endHeight := FindHeight(btcspv.Hash256(oldPeriodEndHeader)) /* NB: retargets should happen at 2016 block intervals */ if endHeight%2016 != 2015 { return false, errors.New("Must provide the last header of the closing difficulty period") - } else if endHeight != startHeight+2015 { + } else if endHeight != startHeight.Add(sdk.NewUint(2015)) { return false, errors.New("Must provide exactly 1 difficulty period") } else if btcspv.ExtractDifficulty(oldPeriodStartHeader) != btcspv.ExtractDifficulty(oldPeriodEndHeader) { return false, errors.New("Period header difficulties do not match") @@ -167,10 +167,10 @@ func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit return false, errors.New("Passed in best is not best known") } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { return false, errors.New("New best is unknown") - } else if !isMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { + } else if !IsMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { return false, errors.New("Ancestor must be heaviest common ancestor") } else if !bytes.Equal( - heaviestFromAncestor(ancestor, currentBest, newBest), + HeaviestFromAncestor(ancestor, currentBest, newBest), newBestDigest) { return false, errors.New("New best hash does not have more work than previous") } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index cad95636..5c02a3f5 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -2,9 +2,11 @@ package types import ( "bytes" + "errors" "strings" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // QueryResGetParent is the payload for a GetParent Query @@ -26,50 +28,40 @@ func IsMostRecentAncestor(ancestor []byte, left []byte, right []byte, limit sdk. if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { return true } + + leftCurrent := left + rightCurrent := right + leftPrev := left + rightPrev := right + + for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { + if bytes.Equal(leftPrev, ancestor) { + leftCurrent = leftPrev // cheap + // TODO: leftPrev = previousBlock[leftPrev] + leftPrev = rightPrev // expensive + } + if bytes.Equal(rightPrev, ancestor) { + rightCurrent = rightPrev // cheap + // TODO: rightPrev = previousBlock[rightPrev] + rightPrev = rightPrev // expensive + } + } + + if bytes.Equal(leftCurrent, rightCurrent) { + return false + } /* NB: If the same, they're a nearer ancestor */ + if !bytes.Equal(leftPrev, rightPrev) { + return false + } /* NB: Both must be ancestor */ return true } -// function _isMostRecentAncestor( -// bytes32 _ancestor, -// bytes32 _left, -// bytes32 _right, -// uint256 _limit -// ) internal view returns (bool) { -// /* NB: sure why not */ -// if (_ancestor == _left && _ancestor == _right) { -// return true; -// } - -// bytes32 _leftCurrent = _left; -// bytes32 _rightCurrent = _right; -// bytes32 _leftPrev = _left; -// bytes32 _rightPrev = _right; - -// for(uint256 i = 0; i < _limit; i = i.add(1)) { -// if (_leftPrev != _ancestor) { -// _leftCurrent = _leftPrev; // cheap -// _leftPrev = previousBlock[_leftPrev]; // expensive -// } -// if (_rightPrev != _ancestor) { -// _rightCurrent = _rightPrev; // cheap -// _rightPrev = previousBlock[_rightPrev]; // expensive -// } -// } -// if (_leftCurrent == _rightCurrent) {return false;} /* NB: If the same, they're a nearer ancestor */ -// if (_leftPrev != _rightPrev) {return false;} /* NB: Both must be ancestor */ -// return true; -// } - // Getter for relayGenesis. // This is an initialization parameter. func GetRelayGenesis() []byte { return relayGenesis } -// function getRelayGenesis() public view returns (bytes32) { -// return relayGenesis; -// } - // Getter for relayGenesis. // This is updated only by calling MarkNewHeaviest func getLastReorgCommonAncestor() []byte { @@ -79,97 +71,88 @@ func getLastReorgCommonAncestor() []byte { // Finds the height of a header by its digest // Will fail if the header is unknown func FindHeight(digest []byte) sdk.Uint { - + height := sdk.NewUint(0) + current := digest + for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { + // TODO: height = blockHeight[current] + height = current + if height.IsZero() { + // TODO: current = previousBlock[current] + current = current + } else { + return height.Add(i) + } + } + // TODO: What does this do? + // revert("unknown block") } -// function _findHeight(bytes32 _digest) internal view returns (uint256) { -// uint256 _height = 0; -// bytes32 _current = _digest; -// for (uint256 i = 0; i < HEIGHT_INTERVAL + 1; i = i.add(1)) { -// _height = blockHeight[_current]; -// if (_height == 0) { -// _current = previousBlock[_current]; -// } else { -// return _height.add(i); -// } -// } -// revert("Unknown block"); -// } - // Finds an ancestor for a block by its digest // Will fail if the header is unknown -func FindAncestor(digest []byte, offset sdk.Uint) []byte { - +func FindAncestor(digest []byte, offset sdk.Uint) ([]byte, error) { + current := digest + for i := sdk.NewUint(0); i.LT(offset); i.Add(sdk.NewUint(1)) { + // current = previousBlock[current] + } + if bytes.Equal(current, bytes.Repeat([]byte{0}, 32)) { + return []byte{0}, errors.New("Unknown ancestor") + } + return current } -// function _findAncestor(bytes32 _digest, uint256 _offset) internal view returns (bytes32) { -// bytes32 _current = _digest; -// for (uint256 i = 0; i < _offset; i = i.add(1)) { -// _current = previousBlock[_current]; -// } -// require(_current != bytes32(0), "Unknown ancestor"); -// return _current; -// } - // Checks if a digest is an ancestor of the current one // Limit the amount of lookups (and thus gas usage) with limit func IsAncestor(ancestor []byte, descendant []byte, limit sdk.Uint) bool { + current := descendant + /* NB: 200 gas/read, so gas is capped at ~200 * limit */ + for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { + if bytes.Equal(current, ancestor) { + return true + } + // current = previousBlock[current] + } return false } -// function _isAncestor(bytes32 _ancestor, bytes32 _descendant, uint256 _limit) internal view returns (bool) { -// bytes32 _current = _descendant; -// /* NB: 200 gas/read, so gas is capped at ~200 * limit */ -// for (uint256 i = 0; i < _limit; i = i.add(1)) { -// if (_current == _ancestor) { -// return true; -// } -// _current = previousBlock[_current]; -// } -// return false; -// } - // Decides which header is heaviest from the ancestor // Does not support reorgs above 2017 blocks (: -func HeaviestFromAncestor(ancestor []byte, left []byte, right []byte) []byte { +func HeaviestFromAncestor(ancestor []byte, left []byte, right []byte) ([]byte, error) { + ancestorHeight := FindHeight(ancestor) + leftHeight := FindHeight(btcspv.Hash256(left)) + rightHeight := FindHeight(btcspv.Hash256(right)) -} + if leftHeight.LT(ancestorHeight) && rightHeight.LT(ancestorHeight) { + return []byte{0}, errors.New("A descendant height is below the ancestor height") + } -// function _heaviestFromAncestor( -// bytes32 _ancestor, -// bytes memory _left, -// bytes memory _right -// ) internal view returns (bytes32) { -// uint256 _ancestorHeight = _findHeight(_ancestor); -// uint256 _leftHeight = _findHeight(_left.hash256()); -// uint256 _rightHeight = _findHeight(_right.hash256()); - -// require( -// _leftHeight >= _ancestorHeight && _rightHeight >= _ancestorHeight, -// "A descendant height is below the ancestor height"); - -// /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ -// uint256 _nextPeriodStartHeight = _ancestorHeight.add(2016).sub(_ancestorHeight % 2016); -// bool _leftInPeriod = _leftHeight < _nextPeriodStartHeight; -// bool _rightInPeriod = _rightHeight < _nextPeriodStartHeight; - -// /* -// NB: -// 1. Left is in a new window, right is in the old window. Left is heavier -// 2. Right is in a new window, left is in the old window. Right is heavier -// 3. Both are in the same window, choose the higher one -// 4. They're in different new windows. Choose the heavier one -// */ -// if (!_leftInPeriod && _rightInPeriod) {return _left.hash256();} -// if (_leftInPeriod && !_rightInPeriod) {return _right.hash256();} -// if (_leftInPeriod && _rightInPeriod) { -// return _leftHeight >= _rightHeight ? _left.hash256() : _right.hash256(); -// } else { // if (!_leftInPeriod && !_rightInPeriod) { -// if (((_leftHeight % 2016).mul(_left.extractDifficulty())) < -// (_rightHeight % 2016).mul(_right.extractDifficulty())) { -// return _right.hash256(); -// } else { -// return _left.hash256(); -// } -// } -// } + /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ + length := sdk.NewUint(2016) + nextPeriodStartHeight := ancestorHeight.Add(length).Sub(ancestorHeight % length) + leftInPeriod := leftHeight.LT(nextPeriodStartHeight) + rightInPeriod := rightHeight.LT(nextPeriodStartHeight) + + /* + NB: + 1. Left is in a new window, right is in the old window. Left is heavier + 2. Right is in a new window, left is in the old window. Right is heavier + 3. Both are in the same window, choose the higher one + 4. They're in different new windows. Choose the heavier one + */ + if !leftInPeriod && rightInPeriod { + return btcspv.Hash256(left), nil + } else if leftInPeriod && !rightInPeriod { + return btcspv.Hash256(right), nil + } else if leftInPeriod && rightInPeriod { + if leftHeight.GTE(rightHeight) { + return btcspv.Hash256(left), nil + } else { + return btcspv.Hash256(right), nil + } + } else { + if (leftHeight % length).Mul(btcspv.ExtractDifficulty(left)).LT((rightHeight % length).Mul(btcspv.ExtractDifficulty(right))) { + return btcspv.Hash256(right), nil + } else { + return btcspv.Hash256(left), nil + } + } +} From ffb42cdab70d6fcfab91b97bf40dcafcb59b9c51 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 13:33:11 -0700 Subject: [PATCH 030/148] added types --- golang/go.mod | 1 + golang/x/relay/types/msgs.go | 37 ++++++++++++++++++++------------- golang/x/relay/types/querier.go | 32 +++++++++++++++++++++------- 3 files changed, 49 insertions(+), 21 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index 363a46ac..ad14c379 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/cosmos/cosmos-sdk v0.37.0 + github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index ea692a50..495f54cf 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -60,17 +60,19 @@ func (msg MsgSetLink) GetSignBytes() []byte { // Route should return the name of the module func (msg MsgSetLink) Route() string { return RouterKey } -// TODO: Add digest type that is 32 bytes long // AddHeaders adds headers to storage after validating. We check // integrity and consistency of the header chain. -func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { +func AddHeaders( + anchor types.RawHeader, + headers types.RawHeader, + internal bool +) (bool, error) { var height sdk.Uint - var header []byte - var currentDigest []byte - var previousDigest []byte = btcspv.Hash256(anchor) + var header types.RawHeader + var currentDigest types.Hash256Digest + var previousDigest := btcspv.Hash256(anchor) target := btcspv.ExtractTarget(headers[0:80]) - // TODO: write findHeight in queries anchorHeight := FindHeight(previousDigest) /* NB: errors if unknown */ extractedTarget := btcspv.ExtractTarget(anchor) if !(internal || extractedTarget == target) { @@ -116,7 +118,7 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { previousDigest = currentDigest } - // TODO: How to do this in go... + // TODO: Use emitExtension in headers.go // emit Extension( // btcspv.Hash256(anchor), // currentDigest); @@ -124,9 +126,13 @@ func AddHeaders(anchor []byte, headers []byte, internal bool) (bool, error) { } // Adds headers to storage, performs additional validation of retarget. -func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []byte, headers []byte) (bool, error) { +func AddHeadersWithRetarget( + oldPeriodStartHeader types.RawHeader, + oldPeriodEndHeader types.RawHeader, + headers []byte +) (bool, error) { /* NB: requires that both blocks are known */ - startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader)) + startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader[:])) endHeight := FindHeight(btcspv.Hash256(oldPeriodEndHeader)) /* NB: retargets should happen at 2016 block intervals */ @@ -146,7 +152,6 @@ func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []by btcspv.ExtractTarget(oldPeriodStartHeader), btcspv.ExtractTimestamp(oldPeriodStartHeader), btcspv.ExtractTimestamp(oldPeriodEndHeader)) - // TODO: Fix next line & if actualTarget&expectedTarget != actualTarget { return false, errors.New("Invalid retarget provided") } @@ -157,12 +162,16 @@ func AddHeadersWithRetarget(oldPeriodStartHeader []byte, oldPeriodEndHeader []by return true, nil } -// TODO: write isMostRecentAncestor and heaviestFromAncestor +// TODO: delete, already in chain.go // Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis -func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit sdk.Uint) (bool, error) { +func MarkNewHeaviest( + ancestor types.Hash256Digest, + currentBest types.Hash256Digest, + newBest types.Hash256Digest, + limit sdk.Uint +) (bool, error) { newBestDigest := btcspv.Hash256(newBest) currentBestDigest := btcspv.Hash256(currentBest) - // TODO: Where is bestKnownDigest coming from? if !bytes.Equal(currentBestDigest, bestKnownDigest) { return false, errors.New("Passed in best is not best known") } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { @@ -176,7 +185,7 @@ func MarkNewHeaviest(ancestor []byte, currentBest []byte, newBest []byte, limit } bestKnownDigest = newBestDigest lastReorgCommonAncestor = ancestor - // TODO: Figure out how to do this in go + // TODO: Use Emit Reorg function in chain.go // emit Reorg( // currentBestDigest, // newBestDigest, diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 5c02a3f5..a26cbdc9 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/bitcoin-spv/golang/btcspv" + "github.com/summa-tx/relays/golang/x/relay/types" ) // QueryResGetParent is the payload for a GetParent Query @@ -24,7 +25,12 @@ var relayGenesis []byte // Checks if a digest is an ancestor of the current one // Limit the amount of lookups (and thus gas usage) with limit -func IsMostRecentAncestor(ancestor []byte, left []byte, right []byte, limit sdk.Uint) bool { +func IsMostRecentAncestor( + ancestor types.Hash256Digest, + left types.Hash256Digest, + right types.Hash256Digest, + limit sdk.Uint +) bool { if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { return true } @@ -56,21 +62,22 @@ func IsMostRecentAncestor(ancestor []byte, left []byte, right []byte, limit sdk. return true } +// TODO: delete? There is already a GetRelayGenesis in querier.go // Getter for relayGenesis. // This is an initialization parameter. -func GetRelayGenesis() []byte { +func GetRelayGenesis() types.Hash256Digest { return relayGenesis } // Getter for relayGenesis. // This is updated only by calling MarkNewHeaviest -func getLastReorgCommonAncestor() []byte { +func getLastReorgCommonAncestor() types.Hash256Digest { return lastReorgCommonAncestor } // Finds the height of a header by its digest // Will fail if the header is unknown -func FindHeight(digest []byte) sdk.Uint { +func FindHeight(digest types.Hash256Digest) sdk.Uint { height := sdk.NewUint(0) current := digest for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { @@ -87,9 +94,10 @@ func FindHeight(digest []byte) sdk.Uint { // revert("unknown block") } +// TODO: delete? Already in links.go // Finds an ancestor for a block by its digest // Will fail if the header is unknown -func FindAncestor(digest []byte, offset sdk.Uint) ([]byte, error) { +func FindAncestor(digest types.Hash256Digest, offset sdk.Uint) ([]byte, error) { current := digest for i := sdk.NewUint(0); i.LT(offset); i.Add(sdk.NewUint(1)) { // current = previousBlock[current] @@ -100,9 +108,14 @@ func FindAncestor(digest []byte, offset sdk.Uint) ([]byte, error) { return current } +// TODO: Delete? Already in links.go // Checks if a digest is an ancestor of the current one // Limit the amount of lookups (and thus gas usage) with limit -func IsAncestor(ancestor []byte, descendant []byte, limit sdk.Uint) bool { +func IsAncestor( + ancestor types.Hash256Digest, + descendant types.Hash256Digest, + limit sdk.Uint +) bool { current := descendant /* NB: 200 gas/read, so gas is capped at ~200 * limit */ for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { @@ -114,9 +127,14 @@ func IsAncestor(ancestor []byte, descendant []byte, limit sdk.Uint) bool { return false } +// TODO: Delete, already in chain.go // Decides which header is heaviest from the ancestor // Does not support reorgs above 2017 blocks (: -func HeaviestFromAncestor(ancestor []byte, left []byte, right []byte) ([]byte, error) { +func HeaviestFromAncestor( + ancestor types.Hash256Digest, + left types.Hash256Digest, + right types.Hash256Digest +) (types.Hash256Digest, error) { ancestorHeight := FindHeight(ancestor) leftHeight := FindHeight(btcspv.Hash256(left)) rightHeight := FindHeight(btcspv.Hash256(right)) From 0c9e73a49f314616d89662d1d25a089075690cfa Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 27 Nov 2019 13:24:20 -0800 Subject: [PATCH 031/148] refactor: walkthrough messages and queries --- golang/README.md | 4 +- golang/go.mod | 1 - golang/x/relay/genesis.go | 13 +- golang/x/relay/keeper/handler.go | 10 + golang/x/relay/keeper/querier.go | 59 +++++- golang/x/relay/types/msgs.go | 284 ++++++++++++------------- golang/x/relay/types/querier.go | 342 ++++++++++++++++--------------- 7 files changed, 397 insertions(+), 316 deletions(-) diff --git a/golang/README.md b/golang/README.md index 151cf661..577b5cd7 100644 --- a/golang/README.md +++ b/golang/README.md @@ -16,6 +16,7 @@ 1. Add new string tag for the new query 1. Add new `query___` function 1. Add new case block to `switch` in `NewQuerier()` +--- DO UP TO HERE FOR NOW --- 1. Add alias type in `x/relay/alias.go` 1. Add to CLI 1. add to `x/relay/client/cli/query.go` @@ -28,7 +29,7 @@ 1. Add msg type in `x/relay/types/msgs.go` 1. Message type is a struct with the arguments 1. Implement `New___()` - 1. Implement `GetSigners()` + 1. Implement `GetSigners()` <--- Ask me about this later 1. Implement `Type()` 1. Implement `ValidateBasic()` 1. Implement `GetSignBytes()` @@ -36,6 +37,7 @@ 1. Add to handler 1. Add new `handle____` function 1. Add new case block to `switch` in `NewHandler()` +--- DO UP TO HERE FOR NOW --- 1. Add aliases in `x/relay/alias.go` 1. Add alisa in `var` block 1. Add alias in `type` block diff --git a/golang/go.mod b/golang/go.mod index ad14c379..363a46ac 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -4,7 +4,6 @@ go 1.12 require ( github.com/cosmos/cosmos-sdk v0.37.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index c92b426f..46407138 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -10,12 +10,12 @@ import ( // GenesisState is the genesis state type GenesisState struct { - Headers []btcspv.BitcoinHeader `json:"headers"` - PeriodStart btcspv.BitcoinHeader `json:"periodStart"` + Headers []BitcoinHeader `json:"headers"` + PeriodStart BitcoinHeader `json:"periodStart"` } // NewGenesisState instantiates a genesis state -func NewGenesisState(headers []btcspv.BitcoinHeader, periodStart btcspv.BitcoinHeader) GenesisState { +func NewGenesisState(headers []BitcoinHeader, periodStart BitcoinHeader) GenesisState { return GenesisState{Headers: headers, PeriodStart: periodStart} } @@ -43,10 +43,11 @@ func ValidateGenesis(data GenesisState) error { } // DefaultGenesisState makes a default empty genesis state +// TODO: set recent block as default func DefaultGenesisState() GenesisState { return GenesisState{ - Headers: []btcspv.BitcoinHeader{}, - PeriodStart: btcspv.BitcoinHeader{}, + Headers: []BitcoinHeader{}, + PeriodStart: BitcoinHeader{}, } } @@ -58,6 +59,8 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.Valid } // ExportGenesis exports the genesis state +// TODO: export GenesisState +// May need special store keys for it func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { panic("Not implemented") } diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 8c62fd59..c7da8b63 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -12,9 +12,19 @@ func NewHandler(keeper Keeper) sdk.Handler { switch msg := msg.(type) { // case types.MsgSetLink: // return handleMsgSetLink(ctx, keeper, msg) + case types.MsgIngestHeaderChain: + return handleMsgIngestHeaderChain(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() } } } + +func handleMsgIngestHeaderChain(ctx sdk.Context, keeper Keeper, msg types.MsgIngestHeaderChain) { + err := k.IngestHeaderChain(ctx, msg.Headers) + if err != nil { + return err.Result() + } + return sdk.Result{} +} diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index acde6a20..6e88e2a8 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -1,21 +1,70 @@ package keeper import ( + "encoding/hex" + + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" abci "github.com/tendermint/tendermint/abci/types" ) -// QueryGetParent is a query string tag for get parent -const QueryGetParent = "getparent" +// QueryIsAncestor is a query string tag for IsAncestor +const QueryIsAncestor = "isancestor" // NewQuerier makes a query routing function func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - // case QueryGetParent: - // return queryGetParent(ctx, path[1:], req, keeper) + case QueryIsAncestor: + return queryIsAncestor(ctx, path[1:], req, keeper) default: - return nil, sdk.ErrUnknownRequest("unknown nameservice query endpoint") + return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } } } + +func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // Take some URL path, parse the arguments out of it, pass it to the keeper, + // and format the result as a QueryResIsAncestor + + // The path I expect here looks like this: + // /getancestor/abcd1234.../second_digest/limit + + // getAncestor is removed by the handler switch/case block above + + // parse the first path item as hex. + // TODO: check that the path is this many items long, error if not + hexDigest := path[0] + digestBytes, err := hex.DecodeString(hexDigest) + if err != nil { + return []byte{}, types.ErrBadHex(types.DefaultCodespace) + } + digestLE := btcspv.NewHash256Digest(digestBytes) + + // parse the second path item as hex. + // TODO: check that the path is this many items long, error if not + ancestorDigest := path[1] + ancestorDigestBytes, err := hex.DecodeString(ancestorDigest) + if err != nil { + return []byte{}, types.ErrBadHex(types.DefaultCodespace) + } + ancestorDigestLE := btcspv.NewHash256Digest(ancestorDigestBytes) + + // TODO: check that the path is this many items long, error if not + limit := 25 // TODO: parse from path, use 240 as default if not in path + + // This calls the keeper with the parsed arguments, and gets an answer + result := keeper.IsAncestor(ctx, ancestorDigestLE, digestLE, limit) + + // Now we format the answer as a response + response := types.QueryResIsAncestor{digestLE, ancestorDigestLE, result} + + // And we serialize that response as JSON + res, err := codec.MarshalJSONIndent(keeper.cdc, response) + if err != nil { + // TODO: better handling + panic("could not marshal result to JSON") + } + return res, nil +} diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 495f54cf..0c4669a0 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -1,22 +1,14 @@ package types import ( - "bytes" "encoding/hex" - "errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // RouterKey is a name for the router const RouterKey = ModuleName // this was defined in your key.go file -const HEIGHT_INTERVAL = 4 - -var bestKnownDigest []byte -var lastReorgCommonAncestor []byte - // MsgSetLink defines a SetLink message type MsgSetLink struct { Address string `json:"signer"` @@ -60,136 +52,152 @@ func (msg MsgSetLink) GetSignBytes() []byte { // Route should return the name of the module func (msg MsgSetLink) Route() string { return RouterKey } -// AddHeaders adds headers to storage after validating. We check -// integrity and consistency of the header chain. -func AddHeaders( - anchor types.RawHeader, - headers types.RawHeader, - internal bool -) (bool, error) { - var height sdk.Uint - var header types.RawHeader - var currentDigest types.Hash256Digest - var previousDigest := btcspv.Hash256(anchor) - - target := btcspv.ExtractTarget(headers[0:80]) - anchorHeight := FindHeight(previousDigest) /* NB: errors if unknown */ - extractedTarget := btcspv.ExtractTarget(anchor) - if !(internal || extractedTarget == target) { - return false, errors.New("Header array length must be divisible by 80") - } - - /* - NB: - 1. check that the header has sufficient work - 2. check that headers are in a coherent chain (no retargets, hash links good) - 3. Store the block connection - 4. Store the height - */ - for i := 0; i < len(headers)/80; i++ { - start := i * 80 - header := headers[start:(start + 80)] - height := anchorHeight.Add(sdk.NewUint(uint64(i + 1))) - currentDigest = btcspv.Hash256(header) - /* - NB: - if the block is already authenticated, we don't need to a work check - Or write anything to state. This saves gas - */ - if bytes.Equal(currentDigest, bytes.Repeat([]byte{0}, 32)) { - if btcspv.BytesToBigUint(btcspv.ReverseEndianness(currentDigest)).LTE(target) { - return false, errors.New("Header work is insufficient") - } - currentDigest = previousDigest - if height%HEIGHT_INTERVAL == 0 { - /* - NB: We store the height only every 4th header to save gas - */ - currentDigest = height - } - } - /* NB: we do still need to make chain level checks tho */ - if !(btcspv.ExtractTarget(header) == target) { - return false, errors.New("Target changed unexpectedly") - } - if !(btcspv.ValidateHeaderPrevHash(header, previousDigest)) { - return false, errors.New("Headers do not form a consistent chain") - } - - previousDigest = currentDigest - } - // TODO: Use emitExtension in headers.go - // emit Extension( - // btcspv.Hash256(anchor), - // currentDigest); - return true, nil +// MsgIngestHeaderChain defines a IngestHeaderChain message +type MsgIngestHeaderChain struct { + Headers []BitcoinHeader `json:"headers"` } -// Adds headers to storage, performs additional validation of retarget. -func AddHeadersWithRetarget( - oldPeriodStartHeader types.RawHeader, - oldPeriodEndHeader types.RawHeader, - headers []byte -) (bool, error) { - /* NB: requires that both blocks are known */ - startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader[:])) - endHeight := FindHeight(btcspv.Hash256(oldPeriodEndHeader)) - - /* NB: retargets should happen at 2016 block intervals */ - if endHeight%2016 != 2015 { - return false, errors.New("Must provide the last header of the closing difficulty period") - } else if endHeight != startHeight.Add(sdk.NewUint(2015)) { - return false, errors.New("Must provide exactly 1 difficulty period") - } else if btcspv.ExtractDifficulty(oldPeriodStartHeader) != btcspv.ExtractDifficulty(oldPeriodEndHeader) { - return false, errors.New("Period header difficulties do not match") - } - - /* NB: This comparison looks weird because header nBits encoding truncates targets */ - - newPeriodStart := headers[0:80] - actualTarget := btcspv.ExtractTarget(newPeriodStart) - expectedTarget := btcspv.RetargetAlgorithm( - btcspv.ExtractTarget(oldPeriodStartHeader), - btcspv.ExtractTimestamp(oldPeriodStartHeader), - btcspv.ExtractTimestamp(oldPeriodEndHeader)) - if actualTarget&expectedTarget != actualTarget { - return false, errors.New("Invalid retarget provided") - } - - // Pass all but the first through to be added - return AddHeaders(oldPeriodEndHeader, headers, true) - - return true, nil -} - -// TODO: delete, already in chain.go -// Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis -func MarkNewHeaviest( - ancestor types.Hash256Digest, - currentBest types.Hash256Digest, - newBest types.Hash256Digest, - limit sdk.Uint -) (bool, error) { - newBestDigest := btcspv.Hash256(newBest) - currentBestDigest := btcspv.Hash256(currentBest) - if !bytes.Equal(currentBestDigest, bestKnownDigest) { - return false, errors.New("Passed in best is not best known") - } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { - return false, errors.New("New best is unknown") - } else if !IsMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { - return false, errors.New("Ancestor must be heaviest common ancestor") - } else if !bytes.Equal( - HeaviestFromAncestor(ancestor, currentBest, newBest), - newBestDigest) { - return false, errors.New("New best hash does not have more work than previous") - } - bestKnownDigest = newBestDigest - lastReorgCommonAncestor = ancestor - // TODO: Use Emit Reorg function in chain.go - // emit Reorg( - // currentBestDigest, - // newBestDigest, - // ancestor); - - return true, nil +// TODO: implement these right +func NewMsgIngestHeaderChain() MsgIngestHeaderChain {} +func (msg MsgIngestHeaderChain) GetSigners() []sdk.AccAddress {} +func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } +func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error {} +func (msg MsgIngestHeaderChain) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +func (msg MsgIngestHeaderChain) Route() string { return RouterKey } + +// // AddHeaders adds headers to storage after validating. We check +// // integrity and consistency of the header chain. +// func AddHeaders( +// anchor types.RawHeader, +// headers types.RawHeader, +// internal bool, +// ) (bool, error) { +// var height sdk.Uint +// var header types.RawHeader +// var currentDigest types.Hash256Digest +// previousDigest := btcspv.Hash256(anchor) +// +// target := btcspv.ExtractTarget(headers[0:80]) +// anchorHeight := FindHeight(previousDigest) /* NB: errors if unknown */ +// extractedTarget := btcspv.ExtractTarget(anchor) +// if !(internal || extractedTarget == target) { +// return false, errors.New("Header array length must be divisible by 80") +// } +// +// /* +// NB: +// 1. check that the header has sufficient work +// 2. check that headers are in a coherent chain (no retargets, hash links good) +// 3. Store the block connection +// 4. Store the height +// */ +// for i := 0; i < len(headers)/80; i++ { +// start := i * 80 +// header := headers[start:(start + 80)] +// height := anchorHeight.Add(sdk.NewUint(uint64(i + 1))) +// currentDigest = btcspv.Hash256(header) +// /* +// NB: +// if the block is already authenticated, we don't need to a work check +// Or write anything to state. This saves gas +// */ +// if bytes.Equal(currentDigest, bytes.Repeat([]byte{0}, 32)) { +// if btcspv.BytesToBigUint(btcspv.ReverseEndianness(currentDigest)).LTE(target) { +// return false, errors.New("Header work is insufficient") +// } +// currentDigest = previousDigest +// if height%HEIGHT_INTERVAL == 0 { +// /* +// NB: We store the height only every 4th header to save gas +// */ +// currentDigest = height +// } +// } +// /* NB: we do still need to make chain level checks tho */ +// if !(btcspv.ExtractTarget(header) == target) { +// return false, errors.New("Target changed unexpectedly") +// } +// if !(btcspv.ValidateHeaderPrevHash(header, previousDigest)) { +// return false, errors.New("Headers do not form a consistent chain") +// } +// +// previousDigest = currentDigest +// } +// // TODO: Use emitExtension in headers.go +// // emit Extension( +// // btcspv.Hash256(anchor), +// // currentDigest); +// return true, nil +// } + +// +// // Adds headers to storage, performs additional validation of retarget. +// func AddHeadersWithRetarget( +// oldPeriodStartHeader types.RawHeader, +// oldPeriodEndHeader types.RawHeader, +// headers []byte, +// ) (bool, error) { +// /* NB: requires that both blocks are known */ +// startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader[:])) +// endHeight := FindHeight(btcspv.Hash256(oldPeriodEndHeader)) +// +// /* NB: retargets should happen at 2016 block intervals */ +// if endHeight%2016 != 2015 { +// return false, errors.New("Must provide the last header of the closing difficulty period") +// } else if endHeight != startHeight.Add(sdk.NewUint(2015)) { +// return false, errors.New("Must provide exactly 1 difficulty period") +// } else if btcspv.ExtractDifficulty(oldPeriodStartHeader) != btcspv.ExtractDifficulty(oldPeriodEndHeader) { +// return false, errors.New("Period header difficulties do not match") +// } +// +// /* NB: This comparison looks weird because header nBits encoding truncates targets */ +// +// newPeriodStart := headers[0:80] +// actualTarget := btcspv.ExtractTarget(newPeriodStart) +// expectedTarget := btcspv.RetargetAlgorithm( +// btcspv.ExtractTarget(oldPeriodStartHeader), +// btcspv.ExtractTimestamp(oldPeriodStartHeader), +// btcspv.ExtractTimestamp(oldPeriodEndHeader)) +// if actualTarget&expectedTarget != actualTarget { +// return false, errors.New("Invalid retarget provided") +// } +// +// // Pass all but the first through to be added +// return AddHeaders(oldPeriodEndHeader, headers, true) +// +// return true, nil +// } +// +// // TODO: delete, already in chain.go +// // Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis +// func MarkNewHeaviest( +// ancestor types.Hash256Digest, +// currentBest types.Hash256Digest, +// newBest types.Hash256Digest, +// limit sdk.Uint, +// ) (bool, error) { +// newBestDigest := btcspv.Hash256(newBest) +// currentBestDigest := btcspv.Hash256(currentBest) +// if !bytes.Equal(currentBestDigest, bestKnownDigest) { +// return false, errors.New("Passed in best is not best known") +// } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { +// return false, errors.New("New best is unknown") +// } else if !IsMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { +// return false, errors.New("Ancestor must be heaviest common ancestor") +// } else if !bytes.Equal( +// HeaviestFromAncestor(ancestor, currentBest, newBest), +// newBestDigest) { +// return false, errors.New("New best hash does not have more work than previous") +// } +// bestKnownDigest = newBestDigest +// lastReorgCommonAncestor = ancestor +// // TODO: Use Emit Reorg function in chain.go +// // emit Reorg( +// // currentBestDigest, +// // newBestDigest, +// // ancestor); +// +// return true, nil +// } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index a26cbdc9..282b541b 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -1,176 +1,186 @@ package types import ( - "bytes" - "errors" + "encoding/hex" + "fmt" "strings" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" - "github.com/summa-tx/relays/golang/x/relay/types" ) -// QueryResGetParent is the payload for a GetParent Query -type QueryResGetParent struct { - Digest string `json:"digest"` - Parent string `json:"parent"` -} - -// implement fmt.Stringer -func (r QueryResGetParent) String() string { - return strings.Join([]string{r.Digest, r.Parent}, "\n") -} - -var relayGenesis []byte - -// Checks if a digest is an ancestor of the current one -// Limit the amount of lookups (and thus gas usage) with limit -func IsMostRecentAncestor( - ancestor types.Hash256Digest, - left types.Hash256Digest, - right types.Hash256Digest, - limit sdk.Uint -) bool { - if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { - return true - } - - leftCurrent := left - rightCurrent := right - leftPrev := left - rightPrev := right - - for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { - if bytes.Equal(leftPrev, ancestor) { - leftCurrent = leftPrev // cheap - // TODO: leftPrev = previousBlock[leftPrev] - leftPrev = rightPrev // expensive - } - if bytes.Equal(rightPrev, ancestor) { - rightCurrent = rightPrev // cheap - // TODO: rightPrev = previousBlock[rightPrev] - rightPrev = rightPrev // expensive - } - } - - if bytes.Equal(leftCurrent, rightCurrent) { - return false - } /* NB: If the same, they're a nearer ancestor */ - if !bytes.Equal(leftPrev, rightPrev) { - return false - } /* NB: Both must be ancestor */ - return true -} - -// TODO: delete? There is already a GetRelayGenesis in querier.go -// Getter for relayGenesis. -// This is an initialization parameter. -func GetRelayGenesis() types.Hash256Digest { - return relayGenesis +// // QueryResGetParent is the payload for a GetParent Query +// type QueryResGetParent struct { +// Digest Hash256Digest `json:"digest"` +// Parent Hash256Digest `json:"parent"` +// } +// +// // implement fmt.Stringer +// func (r QueryResGetParent) String() string { +// return strings.Join([]string{r.Digest, r.Parent}, "\n") +// } + +type QueryResIsAncestor struct { + Digest Hash256Digest `json:"digest"` + ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` + IsAncestor bool `json:isAncestor` } -// Getter for relayGenesis. -// This is updated only by calling MarkNewHeaviest -func getLastReorgCommonAncestor() types.Hash256Digest { - return lastReorgCommonAncestor +func (r QueryResIsAncestor) String() string { + dig := "0x" + hex.EncodeToString(r.Digest[:]) + digAnc := "0x" + hex.EncodeToString(r.ProspectiveAncestor[:]) + res := fmt.Sprintf("%b", r.IsAncestor) + return strings.Join([]string{dig, digAnc, res}, "\n") } -// Finds the height of a header by its digest -// Will fail if the header is unknown -func FindHeight(digest types.Hash256Digest) sdk.Uint { - height := sdk.NewUint(0) - current := digest - for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { - // TODO: height = blockHeight[current] - height = current - if height.IsZero() { - // TODO: current = previousBlock[current] - current = current - } else { - return height.Add(i) - } - } - // TODO: What does this do? - // revert("unknown block") -} - -// TODO: delete? Already in links.go -// Finds an ancestor for a block by its digest -// Will fail if the header is unknown -func FindAncestor(digest types.Hash256Digest, offset sdk.Uint) ([]byte, error) { - current := digest - for i := sdk.NewUint(0); i.LT(offset); i.Add(sdk.NewUint(1)) { - // current = previousBlock[current] - } - if bytes.Equal(current, bytes.Repeat([]byte{0}, 32)) { - return []byte{0}, errors.New("Unknown ancestor") - } - return current -} - -// TODO: Delete? Already in links.go -// Checks if a digest is an ancestor of the current one -// Limit the amount of lookups (and thus gas usage) with limit -func IsAncestor( - ancestor types.Hash256Digest, - descendant types.Hash256Digest, - limit sdk.Uint -) bool { - current := descendant - /* NB: 200 gas/read, so gas is capped at ~200 * limit */ - for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { - if bytes.Equal(current, ancestor) { - return true - } - // current = previousBlock[current] - } - return false -} - -// TODO: Delete, already in chain.go -// Decides which header is heaviest from the ancestor -// Does not support reorgs above 2017 blocks (: -func HeaviestFromAncestor( - ancestor types.Hash256Digest, - left types.Hash256Digest, - right types.Hash256Digest -) (types.Hash256Digest, error) { - ancestorHeight := FindHeight(ancestor) - leftHeight := FindHeight(btcspv.Hash256(left)) - rightHeight := FindHeight(btcspv.Hash256(right)) - - if leftHeight.LT(ancestorHeight) && rightHeight.LT(ancestorHeight) { - return []byte{0}, errors.New("A descendant height is below the ancestor height") - } - - /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ - length := sdk.NewUint(2016) - nextPeriodStartHeight := ancestorHeight.Add(length).Sub(ancestorHeight % length) - leftInPeriod := leftHeight.LT(nextPeriodStartHeight) - rightInPeriod := rightHeight.LT(nextPeriodStartHeight) - - /* - NB: - 1. Left is in a new window, right is in the old window. Left is heavier - 2. Right is in a new window, left is in the old window. Right is heavier - 3. Both are in the same window, choose the higher one - 4. They're in different new windows. Choose the heavier one - */ - if !leftInPeriod && rightInPeriod { - return btcspv.Hash256(left), nil - } else if leftInPeriod && !rightInPeriod { - return btcspv.Hash256(right), nil - } else if leftInPeriod && rightInPeriod { - if leftHeight.GTE(rightHeight) { - return btcspv.Hash256(left), nil - } else { - return btcspv.Hash256(right), nil - } - } else { - if (leftHeight % length).Mul(btcspv.ExtractDifficulty(left)).LT((rightHeight % length).Mul(btcspv.ExtractDifficulty(right))) { - return btcspv.Hash256(right), nil - } else { - return btcspv.Hash256(left), nil - } - } -} +// +// var relayGenesis []byte +// +// // Checks if a digest is an ancestor of the current one +// // Limit the amount of lookups (and thus gas usage) with limit +// func IsMostRecentAncestor( +// ancestor types.Hash256Digest, +// left types.Hash256Digest, +// right types.Hash256Digest, +// limit sdk.Uint +// ) bool { +// if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { +// return true +// } +// +// leftCurrent := left +// rightCurrent := right +// leftPrev := left +// rightPrev := right +// +// for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { +// if bytes.Equal(leftPrev, ancestor) { +// leftCurrent = leftPrev // cheap +// // TODO: leftPrev = previousBlock[leftPrev] +// leftPrev = rightPrev // expensive +// } +// if bytes.Equal(rightPrev, ancestor) { +// rightCurrent = rightPrev // cheap +// // TODO: rightPrev = previousBlock[rightPrev] +// rightPrev = rightPrev // expensive +// } +// } +// +// if bytes.Equal(leftCurrent, rightCurrent) { +// return false +// } /* NB: If the same, they're a nearer ancestor */ +// if !bytes.Equal(leftPrev, rightPrev) { +// return false +// } /* NB: Both must be ancestor */ +// return true +// } +// +// // TODO: delete? There is already a GetRelayGenesis in querier.go +// // Getter for relayGenesis. +// // This is an initialization parameter. +// func GetRelayGenesis() types.Hash256Digest { +// return relayGenesis +// } +// +// // Getter for relayGenesis. +// // This is updated only by calling MarkNewHeaviest +// func getLastReorgCommonAncestor() types.Hash256Digest { +// return lastReorgCommonAncestor +// } +// +// // Finds the height of a header by its digest +// // Will fail if the header is unknown +// func FindHeight(digest types.Hash256Digest) sdk.Uint { +// height := sdk.NewUint(0) +// current := digest +// for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { +// // TODO: height = blockHeight[current] +// height = current +// if height.IsZero() { +// // TODO: current = previousBlock[current] +// current = current +// } else { +// return height.Add(i) +// } +// } +// // TODO: What does this do? +// // revert("unknown block") +// } +// +// // TODO: delete? Already in links.go +// // Finds an ancestor for a block by its digest +// // Will fail if the header is unknown +// func FindAncestor(digest types.Hash256Digest, offset sdk.Uint) ([]byte, error) { +// current := digest +// for i := sdk.NewUint(0); i.LT(offset); i.Add(sdk.NewUint(1)) { +// // current = previousBlock[current] +// } +// if bytes.Equal(current, bytes.Repeat([]byte{0}, 32)) { +// return []byte{0}, errors.New("Unknown ancestor") +// } +// return current +// } +// +// // TODO: Delete? Already in links.go +// // Checks if a digest is an ancestor of the current one +// // Limit the amount of lookups (and thus gas usage) with limit +// func IsAncestor( +// ancestor types.Hash256Digest, +// descendant types.Hash256Digest, +// limit sdk.Uint +// ) bool { +// current := descendant +// /* NB: 200 gas/read, so gas is capped at ~200 * limit */ +// for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { +// if bytes.Equal(current, ancestor) { +// return true +// } +// // current = previousBlock[current] +// } +// return false +// } +// +// // TODO: Delete, already in chain.go +// // Decides which header is heaviest from the ancestor +// // Does not support reorgs above 2017 blocks (: +// func HeaviestFromAncestor( +// ancestor types.Hash256Digest, +// left types.Hash256Digest, +// right types.Hash256Digest +// ) (types.Hash256Digest, error) { +// ancestorHeight := FindHeight(ancestor) +// leftHeight := FindHeight(btcspv.Hash256(left)) +// rightHeight := FindHeight(btcspv.Hash256(right)) +// +// if leftHeight.LT(ancestorHeight) && rightHeight.LT(ancestorHeight) { +// return []byte{0}, errors.New("A descendant height is below the ancestor height") +// } +// +// /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ +// length := sdk.NewUint(2016) +// nextPeriodStartHeight := ancestorHeight.Add(length).Sub(ancestorHeight % length) +// leftInPeriod := leftHeight.LT(nextPeriodStartHeight) +// rightInPeriod := rightHeight.LT(nextPeriodStartHeight) +// +// /* +// NB: +// 1. Left is in a new window, right is in the old window. Left is heavier +// 2. Right is in a new window, left is in the old window. Right is heavier +// 3. Both are in the same window, choose the higher one +// 4. They're in different new windows. Choose the heavier one +// */ +// if !leftInPeriod && rightInPeriod { +// return btcspv.Hash256(left), nil +// } else if leftInPeriod && !rightInPeriod { +// return btcspv.Hash256(right), nil +// } else if leftInPeriod && rightInPeriod { +// if leftHeight.GTE(rightHeight) { +// return btcspv.Hash256(left), nil +// } else { +// return btcspv.Hash256(right), nil +// } +// } else { +// if (leftHeight % length).Mul(btcspv.ExtractDifficulty(left)).LT((rightHeight % length).Mul(btcspv.ExtractDifficulty(right))) { +// return btcspv.Hash256(right), nil +// } else { +// return btcspv.Hash256(left), nil +// } +// } +// } From 158821e709605c1c0c940f92d98e13327e1f9e99 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 16:20:21 -0700 Subject: [PATCH 032/148] finished writing messages --- golang/go.mod | 1 + golang/x/relay/keeper/handler.go | 7 +- golang/x/relay/keeper/headers.go | 8 +- golang/x/relay/types/msgs.go | 273 +++++++++++++++---------------- golang/x/relay/types/querier.go | 11 -- 5 files changed, 139 insertions(+), 161 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index 363a46ac..ad14c379 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/cosmos/cosmos-sdk v0.37.0 + github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index c7da8b63..b64139ed 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -4,14 +4,13 @@ import ( "fmt" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" ) // NewHandler returns a handler for "nameservice" type messages. func NewHandler(keeper Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { switch msg := msg.(type) { - // case types.MsgSetLink: - // return handleMsgSetLink(ctx, keeper, msg) case types.MsgIngestHeaderChain: return handleMsgIngestHeaderChain(ctx, keeper, msg) default: @@ -21,8 +20,8 @@ func NewHandler(keeper Keeper) sdk.Handler { } } -func handleMsgIngestHeaderChain(ctx sdk.Context, keeper Keeper, msg types.MsgIngestHeaderChain) { - err := k.IngestHeaderChain(ctx, msg.Headers) +func handleMsgIngestHeaderChain(ctx sdk.Context, keeper Keeper, msg types.MsgIngestHeaderChain) sdk.Result { + err := keeper.IngestHeaderChain(ctx, msg.Headers) if err != nil { return err.Result() } diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index f8ed13f5..6b476611 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -111,7 +111,7 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in return nil } -func (k Keeper) ingestDiffChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { +func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { if !k.HasHeader(ctx, prevEpochStartLE) { return types.ErrUnknownBlock(types.DefaultCodespace) } @@ -155,7 +155,7 @@ func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader return k.ingestHeaders(ctx, headers, false) } -// IngestDiffChange ingests a chain of headers -func (k Keeper) IngestDiffChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { - return k.ingestDiffChange(ctx, prevEpochStartLE, headers) +// IngestDifficultyChange ingests a chain of headers +func (k Keeper) IngestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { + return k.ingestDifficultyChange(ctx, prevEpochStartLE, headers) } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 0c4669a0..8c9b8f35 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -9,10 +9,12 @@ import ( // RouterKey is a name for the router const RouterKey = ModuleName // this was defined in your key.go file +/***** SetLink *****/ + // MsgSetLink defines a SetLink message type MsgSetLink struct { - Address string `json:"signer"` - Header string `json:"header"` + Signer string `json:"signer"` + Header string `json:"header"` } // NewMsgSetLink is a constructor function for MsgSetLink @@ -25,7 +27,7 @@ func NewMsgSetLink(address, header string, owner sdk.AccAddress) MsgSetLink { // GetSigners defines whose signature is required func (msg MsgSetLink) GetSigners() []sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(msg.Address) + addr, err := sdk.AccAddressFromBech32(msg.Signer) if err != nil { return []sdk.AccAddress{} } @@ -52,152 +54,139 @@ func (msg MsgSetLink) GetSignBytes() []byte { // Route should return the name of the module func (msg MsgSetLink) Route() string { return RouterKey } +/***** IngestHeaderChain *****/ + // MsgIngestHeaderChain defines a IngestHeaderChain message type MsgIngestHeaderChain struct { + Signer string `json:"signer"` Headers []BitcoinHeader `json:"headers"` } -// TODO: implement these right -func NewMsgIngestHeaderChain() MsgIngestHeaderChain {} -func (msg MsgIngestHeaderChain) GetSigners() []sdk.AccAddress {} -func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } -func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error {} +func NewMsgIngestHeaderChain(address string, headers []BitcoinHeader) MsgIngestHeaderChain { + return MsgIngestHeaderChain{ + address, + headers, + } +} + +func (msg MsgIngestHeaderChain) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return []sdk.AccAddress{} + } + return []sdk.AccAddress{addr} +} + +func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } + +func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { + for i := range msg.Headers { + valid, err := msg.Headers[i].Validate() + if valid == false || err != nil { + return ErrBadHeaderLength(DefaultCodespace) + } + } + return nil +} + func (msg MsgIngestHeaderChain) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) + } + func (msg MsgIngestHeaderChain) Route() string { return RouterKey } -// // AddHeaders adds headers to storage after validating. We check -// // integrity and consistency of the header chain. -// func AddHeaders( -// anchor types.RawHeader, -// headers types.RawHeader, -// internal bool, -// ) (bool, error) { -// var height sdk.Uint -// var header types.RawHeader -// var currentDigest types.Hash256Digest -// previousDigest := btcspv.Hash256(anchor) -// -// target := btcspv.ExtractTarget(headers[0:80]) -// anchorHeight := FindHeight(previousDigest) /* NB: errors if unknown */ -// extractedTarget := btcspv.ExtractTarget(anchor) -// if !(internal || extractedTarget == target) { -// return false, errors.New("Header array length must be divisible by 80") -// } -// -// /* -// NB: -// 1. check that the header has sufficient work -// 2. check that headers are in a coherent chain (no retargets, hash links good) -// 3. Store the block connection -// 4. Store the height -// */ -// for i := 0; i < len(headers)/80; i++ { -// start := i * 80 -// header := headers[start:(start + 80)] -// height := anchorHeight.Add(sdk.NewUint(uint64(i + 1))) -// currentDigest = btcspv.Hash256(header) -// /* -// NB: -// if the block is already authenticated, we don't need to a work check -// Or write anything to state. This saves gas -// */ -// if bytes.Equal(currentDigest, bytes.Repeat([]byte{0}, 32)) { -// if btcspv.BytesToBigUint(btcspv.ReverseEndianness(currentDigest)).LTE(target) { -// return false, errors.New("Header work is insufficient") -// } -// currentDigest = previousDigest -// if height%HEIGHT_INTERVAL == 0 { -// /* -// NB: We store the height only every 4th header to save gas -// */ -// currentDigest = height -// } -// } -// /* NB: we do still need to make chain level checks tho */ -// if !(btcspv.ExtractTarget(header) == target) { -// return false, errors.New("Target changed unexpectedly") -// } -// if !(btcspv.ValidateHeaderPrevHash(header, previousDigest)) { -// return false, errors.New("Headers do not form a consistent chain") -// } -// -// previousDigest = currentDigest -// } -// // TODO: Use emitExtension in headers.go -// // emit Extension( -// // btcspv.Hash256(anchor), -// // currentDigest); -// return true, nil -// } - -// -// // Adds headers to storage, performs additional validation of retarget. -// func AddHeadersWithRetarget( -// oldPeriodStartHeader types.RawHeader, -// oldPeriodEndHeader types.RawHeader, -// headers []byte, -// ) (bool, error) { -// /* NB: requires that both blocks are known */ -// startHeight := FindHeight(btcspv.Hash256(oldPeriodStartHeader[:])) -// endHeight := FindHeight(btcspv.Hash256(oldPeriodEndHeader)) -// -// /* NB: retargets should happen at 2016 block intervals */ -// if endHeight%2016 != 2015 { -// return false, errors.New("Must provide the last header of the closing difficulty period") -// } else if endHeight != startHeight.Add(sdk.NewUint(2015)) { -// return false, errors.New("Must provide exactly 1 difficulty period") -// } else if btcspv.ExtractDifficulty(oldPeriodStartHeader) != btcspv.ExtractDifficulty(oldPeriodEndHeader) { -// return false, errors.New("Period header difficulties do not match") -// } -// -// /* NB: This comparison looks weird because header nBits encoding truncates targets */ -// -// newPeriodStart := headers[0:80] -// actualTarget := btcspv.ExtractTarget(newPeriodStart) -// expectedTarget := btcspv.RetargetAlgorithm( -// btcspv.ExtractTarget(oldPeriodStartHeader), -// btcspv.ExtractTimestamp(oldPeriodStartHeader), -// btcspv.ExtractTimestamp(oldPeriodEndHeader)) -// if actualTarget&expectedTarget != actualTarget { -// return false, errors.New("Invalid retarget provided") -// } -// -// // Pass all but the first through to be added -// return AddHeaders(oldPeriodEndHeader, headers, true) -// -// return true, nil -// } -// -// // TODO: delete, already in chain.go -// // Gives a starting point for the relay. We don't check this AT ALL really. Don't use relays with bad genesis -// func MarkNewHeaviest( -// ancestor types.Hash256Digest, -// currentBest types.Hash256Digest, -// newBest types.Hash256Digest, -// limit sdk.Uint, -// ) (bool, error) { -// newBestDigest := btcspv.Hash256(newBest) -// currentBestDigest := btcspv.Hash256(currentBest) -// if !bytes.Equal(currentBestDigest, bestKnownDigest) { -// return false, errors.New("Passed in best is not best known") -// } else if bytes.Equal(newBestDigest, bytes.Repeat([]byte{0}, 32)) { -// return false, errors.New("New best is unknown") -// } else if !IsMostRecentAncestor(ancestor, bestKnownDigest, newBestDigest, limit) { -// return false, errors.New("Ancestor must be heaviest common ancestor") -// } else if !bytes.Equal( -// HeaviestFromAncestor(ancestor, currentBest, newBest), -// newBestDigest) { -// return false, errors.New("New best hash does not have more work than previous") -// } -// bestKnownDigest = newBestDigest -// lastReorgCommonAncestor = ancestor -// // TODO: Use Emit Reorg function in chain.go -// // emit Reorg( -// // currentBestDigest, -// // newBestDigest, -// // ancestor); -// -// return true, nil -// } +/***** IngestDifficultyChange *****/ + +// MsgIngestDifficultyChange defines a IngestDifficultyChange message +type MsgIngestDifficultyChange struct { + Signer string `json:"signer"` + Start Hash256Digest `json:"prevEpochStartLE"` + Headers []BitcoinHeader `json:"headers"` +} + +func NewMsgIngestDifficultyChange(address string, start Hash256Digest, headers []BitcoinHeader) MsgIngestDifficultyChange { + return MsgIngestDifficultyChange{ + address, + start, + headers, + } +} + +func (msg MsgIngestDifficultyChange) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return []sdk.AccAddress{} + } + return []sdk.AccAddress{addr} +} + +func (msg MsgIngestDifficultyChange) Type() string { return "ingest_difficulty_change" } + +func (msg MsgIngestDifficultyChange) ValidateBasic() sdk.Error { + for i := range msg.Headers { + valid, err := msg.Headers[i].Validate() + if valid == false || err != nil { + return ErrBadHeaderLength(DefaultCodespace) + } + } + return nil +} + +func (msg MsgIngestDifficultyChange) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgIngestDifficultyChange) Route() string { return RouterKey } + +/***** MarkNewHeaviest *****/ + +// MsgMarkNewHeaviest defines a MarkNewHeaviest message +type MsgMarkNewHeaviest struct { + Signer string `json:"signer"` + CurrentBest RawHeader `json:"currentBest"` + NewBest RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` + Headers []BitcoinHeader `json:"headers"` +} + +func NewMsgMarkNewHeaviest(address string, currentBest RawHeader, newBest RawHeader, limit uint32, headers []BitcoinHeader) MsgMarkNewHeaviest { + return MsgMarkNewHeaviest{ + address, + currentBest, + newBest, + limit, + headers, + } +} + +func (msg MsgMarkNewHeaviest) GetSigners() []sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(msg.Signer) + if err != nil { + return []sdk.AccAddress{} + } + return []sdk.AccAddress{addr} +} + +func (msg MsgMarkNewHeaviest) Type() string { return "mark_new_heaviest" } + +func (msg MsgMarkNewHeaviest) ValidateBasic() sdk.Error { + for i := range msg.Headers { + valid, err := msg.Headers[i].Validate() + if valid == false || err != nil { + return ErrBadHeaderLength(DefaultCodespace) + } + } + + if len(msg.CurrentBest) != 80 || len(msg.NewBest) != 80 { + return ErrBadHeaderLength(DefaultCodespace) + } + + return nil +} + +func (msg MsgMarkNewHeaviest) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgMarkNewHeaviest) Route() string { return RouterKey } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 282b541b..628aa3eb 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -6,17 +6,6 @@ import ( "strings" ) -// // QueryResGetParent is the payload for a GetParent Query -// type QueryResGetParent struct { -// Digest Hash256Digest `json:"digest"` -// Parent Hash256Digest `json:"parent"` -// } -// -// // implement fmt.Stringer -// func (r QueryResGetParent) String() string { -// return strings.Join([]string{r.Digest, r.Parent}, "\n") -// } - type QueryResIsAncestor struct { Digest Hash256Digest `json:"digest"` ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` From 00ff099b3961137c5fe1feecb6abaa071979ec52 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 16:37:35 -0700 Subject: [PATCH 033/148] added handlers --- golang/x/relay/keeper/handler.go | 20 ++++++ golang/x/relay/types/msgs.go | 21 +++---- golang/x/relay/types/querier.go | 104 +------------------------------ 3 files changed, 28 insertions(+), 117 deletions(-) diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index b64139ed..09efcf1f 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -13,6 +13,10 @@ func NewHandler(keeper Keeper) sdk.Handler { switch msg := msg.(type) { case types.MsgIngestHeaderChain: return handleMsgIngestHeaderChain(ctx, keeper, msg) + case types.MsgIngestDifficultyChange: + return handleMsgIngestDifficultyChange(ctx, keeper, msg) + case types.MsgMarkNewHeaviest: + return handleMsgMarkNewHeaviest(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() @@ -27,3 +31,19 @@ func handleMsgIngestHeaderChain(ctx sdk.Context, keeper Keeper, msg types.MsgIng } return sdk.Result{} } + +func handleMsgIngestDifficultyChange(ctx sdk.Context, keeper Keeper, msg types.MsgIngestDifficultyChange) sdk.Result { + err := keeper.IngestDifficultyChange(ctx, msg.Start, msg.Headers) + if err != nil { + return err.Result() + } + return sdk.Result{} +} + +func handleMsgMarkNewHeaviest(ctx sdk.Context, keeper Keeper, msg types.MsgMarkNewHeaviest) sdk.Result { + err := keeper.MarkNewHeaviest(ctx, msg.Ancestor, msg.CurrentBest, msg.NewBest, msg.Limit) + if err != nil { + return err.Result() + } + return sdk.Result{} +} diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 8c9b8f35..9ebccd8d 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -143,20 +143,20 @@ func (msg MsgIngestDifficultyChange) Route() string { return RouterKey } // MsgMarkNewHeaviest defines a MarkNewHeaviest message type MsgMarkNewHeaviest struct { - Signer string `json:"signer"` - CurrentBest RawHeader `json:"currentBest"` - NewBest RawHeader `json:"newBest"` - Limit uint32 `json:"limit"` - Headers []BitcoinHeader `json:"headers"` + Signer string `json:"signer"` + Ancestor Hash256Digest `json:"ancestor"` + CurrentBest RawHeader `json:"currentBest"` + NewBest RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` } -func NewMsgMarkNewHeaviest(address string, currentBest RawHeader, newBest RawHeader, limit uint32, headers []BitcoinHeader) MsgMarkNewHeaviest { +func NewMsgMarkNewHeaviest(address string, ancestor Hash256Digest, currentBest RawHeader, newBest RawHeader, limit uint32) MsgMarkNewHeaviest { return MsgMarkNewHeaviest{ address, + ancestor, currentBest, newBest, limit, - headers, } } @@ -171,13 +171,6 @@ func (msg MsgMarkNewHeaviest) GetSigners() []sdk.AccAddress { func (msg MsgMarkNewHeaviest) Type() string { return "mark_new_heaviest" } func (msg MsgMarkNewHeaviest) ValidateBasic() sdk.Error { - for i := range msg.Headers { - valid, err := msg.Headers[i].Validate() - if valid == false || err != nil { - return ErrBadHeaderLength(DefaultCodespace) - } - } - if len(msg.CurrentBest) != 80 || len(msg.NewBest) != 80 { return ErrBadHeaderLength(DefaultCodespace) } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 628aa3eb..fbe1d9db 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -9,7 +9,7 @@ import ( type QueryResIsAncestor struct { Digest Hash256Digest `json:"digest"` ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` - IsAncestor bool `json:isAncestor` + IsAncestor bool `json:"isAncestor"` } func (r QueryResIsAncestor) String() string { @@ -19,9 +19,6 @@ func (r QueryResIsAncestor) String() string { return strings.Join([]string{dig, digAnc, res}, "\n") } -// -// var relayGenesis []byte -// // // Checks if a digest is an ancestor of the current one // // Limit the amount of lookups (and thus gas usage) with limit // func IsMostRecentAncestor( @@ -42,12 +39,10 @@ func (r QueryResIsAncestor) String() string { // for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { // if bytes.Equal(leftPrev, ancestor) { // leftCurrent = leftPrev // cheap -// // TODO: leftPrev = previousBlock[leftPrev] // leftPrev = rightPrev // expensive // } // if bytes.Equal(rightPrev, ancestor) { // rightCurrent = rightPrev // cheap -// // TODO: rightPrev = previousBlock[rightPrev] // rightPrev = rightPrev // expensive // } // } @@ -61,115 +56,18 @@ func (r QueryResIsAncestor) String() string { // return true // } // -// // TODO: delete? There is already a GetRelayGenesis in querier.go -// // Getter for relayGenesis. -// // This is an initialization parameter. -// func GetRelayGenesis() types.Hash256Digest { -// return relayGenesis -// } -// -// // Getter for relayGenesis. -// // This is updated only by calling MarkNewHeaviest -// func getLastReorgCommonAncestor() types.Hash256Digest { -// return lastReorgCommonAncestor -// } -// // // Finds the height of a header by its digest // // Will fail if the header is unknown // func FindHeight(digest types.Hash256Digest) sdk.Uint { // height := sdk.NewUint(0) // current := digest // for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { -// // TODO: height = blockHeight[current] // height = current // if height.IsZero() { -// // TODO: current = previousBlock[current] // current = current // } else { // return height.Add(i) // } // } -// // TODO: What does this do? // // revert("unknown block") // } -// -// // TODO: delete? Already in links.go -// // Finds an ancestor for a block by its digest -// // Will fail if the header is unknown -// func FindAncestor(digest types.Hash256Digest, offset sdk.Uint) ([]byte, error) { -// current := digest -// for i := sdk.NewUint(0); i.LT(offset); i.Add(sdk.NewUint(1)) { -// // current = previousBlock[current] -// } -// if bytes.Equal(current, bytes.Repeat([]byte{0}, 32)) { -// return []byte{0}, errors.New("Unknown ancestor") -// } -// return current -// } -// -// // TODO: Delete? Already in links.go -// // Checks if a digest is an ancestor of the current one -// // Limit the amount of lookups (and thus gas usage) with limit -// func IsAncestor( -// ancestor types.Hash256Digest, -// descendant types.Hash256Digest, -// limit sdk.Uint -// ) bool { -// current := descendant -// /* NB: 200 gas/read, so gas is capped at ~200 * limit */ -// for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { -// if bytes.Equal(current, ancestor) { -// return true -// } -// // current = previousBlock[current] -// } -// return false -// } -// -// // TODO: Delete, already in chain.go -// // Decides which header is heaviest from the ancestor -// // Does not support reorgs above 2017 blocks (: -// func HeaviestFromAncestor( -// ancestor types.Hash256Digest, -// left types.Hash256Digest, -// right types.Hash256Digest -// ) (types.Hash256Digest, error) { -// ancestorHeight := FindHeight(ancestor) -// leftHeight := FindHeight(btcspv.Hash256(left)) -// rightHeight := FindHeight(btcspv.Hash256(right)) -// -// if leftHeight.LT(ancestorHeight) && rightHeight.LT(ancestorHeight) { -// return []byte{0}, errors.New("A descendant height is below the ancestor height") -// } -// -// /* NB: we can shortcut if one block is in a new difficulty window and the other isn't */ -// length := sdk.NewUint(2016) -// nextPeriodStartHeight := ancestorHeight.Add(length).Sub(ancestorHeight % length) -// leftInPeriod := leftHeight.LT(nextPeriodStartHeight) -// rightInPeriod := rightHeight.LT(nextPeriodStartHeight) -// -// /* -// NB: -// 1. Left is in a new window, right is in the old window. Left is heavier -// 2. Right is in a new window, left is in the old window. Right is heavier -// 3. Both are in the same window, choose the higher one -// 4. They're in different new windows. Choose the heavier one -// */ -// if !leftInPeriod && rightInPeriod { -// return btcspv.Hash256(left), nil -// } else if leftInPeriod && !rightInPeriod { -// return btcspv.Hash256(right), nil -// } else if leftInPeriod && rightInPeriod { -// if leftHeight.GTE(rightHeight) { -// return btcspv.Hash256(left), nil -// } else { -// return btcspv.Hash256(right), nil -// } -// } else { -// if (leftHeight % length).Mul(btcspv.ExtractDifficulty(left)).LT((rightHeight % length).Mul(btcspv.ExtractDifficulty(right))) { -// return btcspv.Hash256(right), nil -// } else { -// return btcspv.Hash256(left), nil -// } -// } -// } From 0579858a6d1e9a48932279c777eb83557ba51a42 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 16:55:14 -0700 Subject: [PATCH 034/148] deleted old code, fixed errors in querier.go --- golang/x/relay/keeper/querier.go | 29 +++++++++++------ golang/x/relay/types/querier.go | 55 +------------------------------- 2 files changed, 21 insertions(+), 63 deletions(-) diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index 6e88e2a8..9fa6be08 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/bitcoin-spv/golang/btcspv" + "github.com/summa-tx/relays/golang/x/relay/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -36,33 +37,43 @@ func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keep // parse the first path item as hex. // TODO: check that the path is this many items long, error if not hexDigest := path[0] - digestBytes, err := hex.DecodeString(hexDigest) - if err != nil { + digestBytes, decodeErr := hex.DecodeString(hexDigest) + if decodeErr != nil { return []byte{}, types.ErrBadHex(types.DefaultCodespace) } - digestLE := btcspv.NewHash256Digest(digestBytes) + digestLE, newDigestErr := btcspv.NewHash256Digest(digestBytes) + if newDigestErr != nil { + return []byte{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) + } // parse the second path item as hex. // TODO: check that the path is this many items long, error if not ancestorDigest := path[1] - ancestorDigestBytes, err := hex.DecodeString(ancestorDigest) + ancestorDigestBytes, decodeErr := hex.DecodeString(ancestorDigest) if err != nil { return []byte{}, types.ErrBadHex(types.DefaultCodespace) } - ancestorDigestLE := btcspv.NewHash256Digest(ancestorDigestBytes) + ancestorDigestLE, newDigestErr := btcspv.NewHash256Digest(ancestorDigestBytes) + if newDigestErr != nil { + return []byte{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) + } // TODO: check that the path is this many items long, error if not - limit := 25 // TODO: parse from path, use 240 as default if not in path + limit := uint32(25) // TODO: parse from path, use 240 as default if not in path // This calls the keeper with the parsed arguments, and gets an answer result := keeper.IsAncestor(ctx, ancestorDigestLE, digestLE, limit) // Now we format the answer as a response - response := types.QueryResIsAncestor{digestLE, ancestorDigestLE, result} + response := types.QueryResIsAncestor{ + Digest: digestLE, + ProspectiveAncestor: ancestorDigestLE, + IsAncestor: result, + } // And we serialize that response as JSON - res, err := codec.MarshalJSONIndent(keeper.cdc, response) - if err != nil { + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { // TODO: better handling panic("could not marshal result to JSON") } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index fbe1d9db..b3b788ce 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -15,59 +15,6 @@ type QueryResIsAncestor struct { func (r QueryResIsAncestor) String() string { dig := "0x" + hex.EncodeToString(r.Digest[:]) digAnc := "0x" + hex.EncodeToString(r.ProspectiveAncestor[:]) - res := fmt.Sprintf("%b", r.IsAncestor) + res := fmt.Sprintf("%t\n", r.IsAncestor) return strings.Join([]string{dig, digAnc, res}, "\n") } - -// // Checks if a digest is an ancestor of the current one -// // Limit the amount of lookups (and thus gas usage) with limit -// func IsMostRecentAncestor( -// ancestor types.Hash256Digest, -// left types.Hash256Digest, -// right types.Hash256Digest, -// limit sdk.Uint -// ) bool { -// if bytes.Equal(ancestor, left) && bytes.Equal(ancestor, right) { -// return true -// } -// -// leftCurrent := left -// rightCurrent := right -// leftPrev := left -// rightPrev := right -// -// for i := sdk.NewUint(0); i.LT(limit); i.Add(sdk.NewUint(1)) { -// if bytes.Equal(leftPrev, ancestor) { -// leftCurrent = leftPrev // cheap -// leftPrev = rightPrev // expensive -// } -// if bytes.Equal(rightPrev, ancestor) { -// rightCurrent = rightPrev // cheap -// rightPrev = rightPrev // expensive -// } -// } -// -// if bytes.Equal(leftCurrent, rightCurrent) { -// return false -// } /* NB: If the same, they're a nearer ancestor */ -// if !bytes.Equal(leftPrev, rightPrev) { -// return false -// } /* NB: Both must be ancestor */ -// return true -// } -// -// // Finds the height of a header by its digest -// // Will fail if the header is unknown -// func FindHeight(digest types.Hash256Digest) sdk.Uint { -// height := sdk.NewUint(0) -// current := digest -// for i := sdk.NewUint(0); i.LT(sdk.NewUint(HEIGHT_INTERVAL + 1)); i = i.Add(sdk.NewUint(1)) { -// height = current -// if height.IsZero() { -// current = current -// } else { -// return height.Add(i) -// } -// } -// // revert("unknown block") -// } From b07cf4f3db91be104f2e0fe3138893ad84ddb4f2 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 18:35:41 -0700 Subject: [PATCH 035/148] fixed stuff --- golang/app.go | 12 +++---- golang/x/relay/keeper/handler.go | 16 +++++++--- golang/x/relay/types/msgs.go | 55 +++++++++++--------------------- 3 files changed, 36 insertions(+), 47 deletions(-) diff --git a/golang/app.go b/golang/app.go index 8e36e90d..069ad292 100644 --- a/golang/app.go +++ b/golang/app.go @@ -1,8 +1,8 @@ package relay import ( - "os" "encoding/json" + "os" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/module" @@ -18,12 +18,12 @@ import ( "github.com/summa-tx/relays/golang/x/relay" - tmtypes "github.com/tendermint/tendermint/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" - abci "github.com/tendermint/tendermint/abci/types" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" ) @@ -77,7 +77,7 @@ type relayApp struct { supplyKeeper supply.Keeper paramsKeeper params.Keeper - relayKeeper relay.Keeper + relayKeeper relay.Keeper // Module Manager mm *module.Manager @@ -190,7 +190,7 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA app.slashingKeeper.Hooks()), ) - // The NameserviceKeeper is the Keeper from the module for this tutorial + // The RelayKeeper is the Keeper from the module for this tutorial // It handles interactions with the namestore app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 09efcf1f..d8c4329e 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -7,7 +7,7 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) -// NewHandler returns a handler for "nameservice" type messages. +// NewHandler returns a handler for relay type messages. func NewHandler(keeper Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { switch msg := msg.(type) { @@ -18,7 +18,7 @@ func NewHandler(keeper Keeper) sdk.Handler { case types.MsgMarkNewHeaviest: return handleMsgMarkNewHeaviest(ctx, keeper, msg) default: - errMsg := fmt.Sprintf("Unrecognized nameservice Msg type: %v", msg.Type()) + errMsg := fmt.Sprintf("Unrecognized relay Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() } } @@ -29,7 +29,9 @@ func handleMsgIngestHeaderChain(ctx sdk.Context, keeper Keeper, msg types.MsgIng if err != nil { return err.Result() } - return sdk.Result{} + return sdk.Result{ + Events: ctx.EventManager().Events(), + } } func handleMsgIngestDifficultyChange(ctx sdk.Context, keeper Keeper, msg types.MsgIngestDifficultyChange) sdk.Result { @@ -37,7 +39,9 @@ func handleMsgIngestDifficultyChange(ctx sdk.Context, keeper Keeper, msg types.M if err != nil { return err.Result() } - return sdk.Result{} + return sdk.Result{ + Events: ctx.EventManager().Events(), + } } func handleMsgMarkNewHeaviest(ctx sdk.Context, keeper Keeper, msg types.MsgMarkNewHeaviest) sdk.Result { @@ -45,5 +49,7 @@ func handleMsgMarkNewHeaviest(ctx sdk.Context, keeper Keeper, msg types.MsgMarkN if err != nil { return err.Result() } - return sdk.Result{} + return sdk.Result{ + Events: ctx.EventManager().Events(), + } } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 9ebccd8d..23b9c515 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -13,12 +13,12 @@ const RouterKey = ModuleName // this was defined in your key.go file // MsgSetLink defines a SetLink message type MsgSetLink struct { - Signer string `json:"signer"` - Header string `json:"header"` + Signer sdk.AccAddress `json:"signer"` + Header string `json:"header"` } // NewMsgSetLink is a constructor function for MsgSetLink -func NewMsgSetLink(address, header string, owner sdk.AccAddress) MsgSetLink { +func NewMsgSetLink(address sdk.AccAddress, header string, owner sdk.AccAddress) MsgSetLink { return MsgSetLink{ address, header, @@ -27,11 +27,7 @@ func NewMsgSetLink(address, header string, owner sdk.AccAddress) MsgSetLink { // GetSigners defines whose signature is required func (msg MsgSetLink) GetSigners() []sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(msg.Signer) - if err != nil { - return []sdk.AccAddress{} - } - return []sdk.AccAddress{addr} + return []sdk.AccAddress{msg.Signer} } // Type should return the action @@ -41,7 +37,7 @@ func (msg MsgSetLink) Type() string { return "set_link" } func (msg MsgSetLink) ValidateBasic() sdk.Error { b, err := hex.DecodeString(msg.Header) if err != nil || len(b) != 80 { - return ErrBadHeaderLength(DefaultCodespace) + return FromBTCSPVError(DefaultCodespace, err) } return nil } @@ -58,11 +54,11 @@ func (msg MsgSetLink) Route() string { return RouterKey } // MsgIngestHeaderChain defines a IngestHeaderChain message type MsgIngestHeaderChain struct { - Signer string `json:"signer"` + Signer sdk.AccAddress `json:"signer"` Headers []BitcoinHeader `json:"headers"` } -func NewMsgIngestHeaderChain(address string, headers []BitcoinHeader) MsgIngestHeaderChain { +func NewMsgIngestHeaderChain(address sdk.AccAddress, headers []BitcoinHeader) MsgIngestHeaderChain { return MsgIngestHeaderChain{ address, headers, @@ -70,11 +66,7 @@ func NewMsgIngestHeaderChain(address string, headers []BitcoinHeader) MsgIngestH } func (msg MsgIngestHeaderChain) GetSigners() []sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(msg.Signer) - if err != nil { - return []sdk.AccAddress{} - } - return []sdk.AccAddress{addr} + return []sdk.AccAddress{msg.Signer} } func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } @@ -83,7 +75,7 @@ func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() if valid == false || err != nil { - return ErrBadHeaderLength(DefaultCodespace) + return FromBTCSPVError(DefaultCodespace, err) } } return nil @@ -91,7 +83,6 @@ func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { func (msg MsgIngestHeaderChain) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) - } func (msg MsgIngestHeaderChain) Route() string { return RouterKey } @@ -100,12 +91,12 @@ func (msg MsgIngestHeaderChain) Route() string { return RouterKey } // MsgIngestDifficultyChange defines a IngestDifficultyChange message type MsgIngestDifficultyChange struct { - Signer string `json:"signer"` + Signer sdk.AccAddress `json:"signer"` Start Hash256Digest `json:"prevEpochStartLE"` Headers []BitcoinHeader `json:"headers"` } -func NewMsgIngestDifficultyChange(address string, start Hash256Digest, headers []BitcoinHeader) MsgIngestDifficultyChange { +func NewMsgIngestDifficultyChange(address sdk.AccAddress, start Hash256Digest, headers []BitcoinHeader) MsgIngestDifficultyChange { return MsgIngestDifficultyChange{ address, start, @@ -114,11 +105,7 @@ func NewMsgIngestDifficultyChange(address string, start Hash256Digest, headers [ } func (msg MsgIngestDifficultyChange) GetSigners() []sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(msg.Signer) - if err != nil { - return []sdk.AccAddress{} - } - return []sdk.AccAddress{addr} + return []sdk.AccAddress{msg.Signer} } func (msg MsgIngestDifficultyChange) Type() string { return "ingest_difficulty_change" } @@ -143,14 +130,14 @@ func (msg MsgIngestDifficultyChange) Route() string { return RouterKey } // MsgMarkNewHeaviest defines a MarkNewHeaviest message type MsgMarkNewHeaviest struct { - Signer string `json:"signer"` - Ancestor Hash256Digest `json:"ancestor"` - CurrentBest RawHeader `json:"currentBest"` - NewBest RawHeader `json:"newBest"` - Limit uint32 `json:"limit"` + Signer sdk.AccAddress `json:"signer"` + Ancestor Hash256Digest `json:"ancestor"` + CurrentBest RawHeader `json:"currentBest"` + NewBest RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` } -func NewMsgMarkNewHeaviest(address string, ancestor Hash256Digest, currentBest RawHeader, newBest RawHeader, limit uint32) MsgMarkNewHeaviest { +func NewMsgMarkNewHeaviest(address sdk.AccAddress, ancestor Hash256Digest, currentBest RawHeader, newBest RawHeader, limit uint32) MsgMarkNewHeaviest { return MsgMarkNewHeaviest{ address, ancestor, @@ -161,11 +148,7 @@ func NewMsgMarkNewHeaviest(address string, ancestor Hash256Digest, currentBest R } func (msg MsgMarkNewHeaviest) GetSigners() []sdk.AccAddress { - addr, err := sdk.AccAddressFromBech32(msg.Signer) - if err != nil { - return []sdk.AccAddress{} - } - return []sdk.AccAddress{addr} + return []sdk.AccAddress{msg.Signer} } func (msg MsgMarkNewHeaviest) Type() string { return "mark_new_heaviest" } From a704ec4575ed24d868dad7d5d2f78cc86a465c21 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Wed, 27 Nov 2019 18:41:26 -0700 Subject: [PATCH 036/148] missed btcspv error --- golang/x/relay/types/msgs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 23b9c515..9b0b1261 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -114,7 +114,7 @@ func (msg MsgIngestDifficultyChange) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() if valid == false || err != nil { - return ErrBadHeaderLength(DefaultCodespace) + return FromBTCSPVError(DefaultCodespace, err) } } return nil From 641a3241de8b00aff96a2dc725f5de625744d802 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Fri, 29 Nov 2019 15:38:04 -0700 Subject: [PATCH 037/148] fix errors, fix typos --- golang/app.go | 2 +- golang/cmd/relaycli/main.go | 4 ++-- golang/x/relay/client/cli/tx.go | 3 ++- golang/x/relay/client/rest/tx.go | 14 +++++--------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/golang/app.go b/golang/app.go index 069ad292..491d7bbf 100644 --- a/golang/app.go +++ b/golang/app.go @@ -191,7 +191,7 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA ) // The RelayKeeper is the Keeper from the module for this tutorial - // It handles interactions with the namestore + // It handles interactions with the store app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], app.cdc, diff --git a/golang/cmd/relaycli/main.go b/golang/cmd/relaycli/main.go index 86de917c..4d98707e 100644 --- a/golang/cmd/relaycli/main.go +++ b/golang/cmd/relaycli/main.go @@ -32,8 +32,8 @@ func main() { config.Seal() rootCmd := &cobra.Command{ - Use: "nscli", - Short: "nameservice Client", + Use: "rcli", + Short: "relay Client", } // Add --chain-id to persistent flags and mark it required diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index ea09b398..54c59cba 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -40,7 +40,8 @@ func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - msg := types.NewMsgSetLink(args[1], args[0], cliCtx.GetFromAddress()) + addr := sdk.AccAddress(args[1]) + msg := types.NewMsgSetLink(addr, args[0], cliCtx.GetFromAddress()) err := msg.ValidateBasic() if err != nil { return err diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index b8e41081..b2be731f 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -13,9 +13,9 @@ import ( ) type setLinkReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Header string `json:"header"` - Sender string `json:"sender"` + BaseReq rest.BaseReq `json:"base_req"` + Header string `json:"header"` + Sender sdk.AccAddress `json:"sender"` } func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { @@ -32,15 +32,11 @@ func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { return } - addr, err := sdk.AccAddressFromBech32(req.Sender) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } + addr := sdk.AccAddress(req.Sender) // create the message msg := types.NewMsgSetLink(req.Sender, req.Header, addr) - err = msg.ValidateBasic() + err := msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) From 89e589fc760d38184aa49d96ede464b995aeb5ed Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Fri, 29 Nov 2019 16:18:11 -0700 Subject: [PATCH 038/148] fixed missing type error in query.go --- golang/x/relay/client/cli/query.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index 5fa22f55..db7c2b24 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -7,9 +7,18 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/spf13/cobra" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" ) +type QueryResGetParent struct { + Digest btcspv.Hash256Digest `json:"digest"` +} + +func (d *QueryResGetParent) String() string { + return fmt.Sprintf("%d", d.Digest) +} + // GetQueryCmd sets up query CLI commands func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { relayQueryCommand := &cobra.Command{ @@ -41,9 +50,9 @@ func GetCommandGetParent(queryRoute string, cdc *codec.Codec) *cobra.Command { return nil } - var out types.QueryResGetParent + var out QueryResGetParent cdc.MustUnmarshalJSON(res, &out) - return cliCtx.PrintOutput(out) + return cliCtx.PrintOutput(&out) }, } } From 081d5fac4a338fe7a350b256136928ad1f885836 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sat, 30 Nov 2019 11:39:45 -0800 Subject: [PATCH 039/148] tests: scaffolding for keeper tests, break out validation into stateless functions --- golang/go.mod | 2 +- golang/x/relay/keeper/headers.go | 52 +++++++++++++++++--------- golang/x/relay/keeper/keeper_test.go | 56 ++++++++++++++++++++++++++++ golang/x/relay/keeper/querier.go | 2 +- testVectors.json | 11 ++++++ 5 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 golang/x/relay/keeper/keeper_test.go create mode 100644 testVectors.json diff --git a/golang/go.mod b/golang/go.mod index ad14c379..3671ab9a 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -4,10 +4,10 @@ go 1.12 require ( github.com/cosmos/cosmos-sdk v0.37.0 - github.com/go-logfmt/logfmt v0.4.0 // indirect github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 + github.com/stretchr/testify v1.3.0 github.com/summa-tx/bitcoin-spv/golang v1.2.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 6b476611..0ae19ef4 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -56,12 +56,7 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store.Set(header.HashLE[:], buf) } -func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { - if !k.HasHeader(ctx, headers[0].PrevHashLE) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } - - anchor := k.GetHeader(ctx, headers[0].PrevHashLE) +func validateHeaderChainForIngestion(anchor types.BitcoinHeader, headers []types.BitcoinHeader, internal, isMainnet bool) sdk.Error { prev := anchor // scratchpad, we change this later // On internal call, use the header chain target @@ -88,7 +83,7 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in // ensure expectedTarget doesn't change // it's allowed to change if the relay is in testnet mode - if k.IsMainNet && btcspv.ExtractTarget(header.Raw) != expectedTarget { + if isMainnet && btcspv.ExtractTarget(header.Raw) != expectedTarget { return types.ErrUnexptectedRetarget(types.DefaultCodespace) } @@ -104,25 +99,29 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in return types.FromBTCSPVError(types.DefaultCodespace, err) } - for _, header := range headers { - k.setLink(ctx, header) - k.ingestHeader(ctx, header) - } return nil } -func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { - if !k.HasHeader(ctx, prevEpochStartLE) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } +func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { if !k.HasHeader(ctx, headers[0].PrevHashLE) { return types.ErrUnknownBlock(types.DefaultCodespace) } - // Find the anchor in our store - prevEpochStart := k.GetHeader(ctx, prevEpochStartLE) anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + err := validateHeaderChainForIngestion(anchor, headers, internal, k.IsMainNet) + if err != nil { + return err + } + + for _, header := range headers { + k.setLink(ctx, header) + k.ingestHeader(ctx, header) + } + return nil +} + +func validateDifficultyChangeForIngestion(prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader, prevEpochStart, anchor types.BitcoinHeader) sdk.Error { if anchor.Height%2016 != 2015 { return types.ErrWrongEnd(types.DefaultCodespace) } @@ -145,6 +144,25 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H if !compareTargets(expectedTarget, actualTarget) { return types.ErrBadRetarget(types.DefaultCodespace) } + return nil +} + +func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { + if !k.HasHeader(ctx, prevEpochStartLE) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + if !k.HasHeader(ctx, headers[0].PrevHashLE) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + + // Find the anchor in our store + prevEpochStart := k.GetHeader(ctx, prevEpochStartLE) + anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + + err := validateDifficultyChangeForIngestion(prevEpochStartLE, headers, prevEpochStart, anchor) + if err != nil { + return err + } return k.ingestHeaders(ctx, headers, true) } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go new file mode 100644 index 00000000..221806f2 --- /dev/null +++ b/golang/x/relay/keeper/keeper_test.go @@ -0,0 +1,56 @@ +package keeper + +import ( + "encoding/json" + "io/ioutil" + "log" + "os" + "testing" + + "github.com/stretchr/testify/suite" +) + +type LinkTest struct{} + +type ChainTest struct{} + +type HeaderTest struct{} + +type KeeperTestCases struct { + LinkTestCases []LinkTest `json:"link"` + HeaderTestCases []HeaderTest `json:"header"` + ChainTestCases []ChainTest `json:"chain"` +} + +type KeeperSuite struct { + suite.Suite + Fixtures KeeperTestCases +} + +func logIfErr(err error) { + if err != nil { + log.Fatal(err) + } +} + +// Runs the whole test suite +func TestKeeper(t *testing.T) { + jsonFile, err := os.Open("../../../../testVectors.json") + defer jsonFile.Close() + logIfErr(err) + + byteValue, err := ioutil.ReadAll(jsonFile) + logIfErr(err) + + var fixtures KeeperTestCases + json.Unmarshal([]byte(byteValue), &fixtures) + + keeperSuite := new(KeeperSuite) + keeperSuite.Fixtures = fixtures + + suite.Run(t, keeperSuite) +} + +func (suite *KeeperSuite) TestGetPrefixStore() { + +} diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index 9fa6be08..d25891a2 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -50,7 +50,7 @@ func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keep // TODO: check that the path is this many items long, error if not ancestorDigest := path[1] ancestorDigestBytes, decodeErr := hex.DecodeString(ancestorDigest) - if err != nil { + if decodeErr != nil { return []byte{}, types.ErrBadHex(types.DefaultCodespace) } ancestorDigestLE, newDigestErr := btcspv.NewHash256Digest(ancestorDigestBytes) diff --git a/testVectors.json b/testVectors.json new file mode 100644 index 00000000..1a2ba9f9 --- /dev/null +++ b/testVectors.json @@ -0,0 +1,11 @@ +{ + "link": [ + {} + ], + "header": [ + {} + ], + "chain": [ + {} + ] +} From a8d27d7451e3027ecbf8ce8c77ff72b4fae16fd0 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sat, 30 Nov 2019 14:52:45 -0800 Subject: [PATCH 040/148] tests: first test for headers.go --- golang/x/relay/keeper/headers.go | 13 +- golang/x/relay/keeper/headers_test.go | 19 +++ golang/x/relay/keeper/keeper_test.go | 33 ++++- golang/x/relay/types/errors.go | 79 +++++++--- testVectors.json | 202 +++++++++++++++++++++++++- 5 files changed, 306 insertions(+), 40 deletions(-) create mode 100644 golang/x/relay/keeper/headers_test.go diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 0ae19ef4..f28361ea 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -56,7 +56,7 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store.Set(header.HashLE[:], buf) } -func validateHeaderChainForIngestion(anchor types.BitcoinHeader, headers []types.BitcoinHeader, internal, isMainnet bool) sdk.Error { +func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHeader, internal, isMainnet bool) sdk.Error { prev := anchor // scratchpad, we change this later // On internal call, use the header chain target @@ -64,7 +64,6 @@ func validateHeaderChainForIngestion(anchor types.BitcoinHeader, headers []types if internal { expectedTarget = btcspv.ExtractTarget(headers[0].Raw) } - // allocate memory for raw anchor + all headers raw := make([]byte, 80*(len(headers)+1)) copy(raw[0:80], anchor.Raw[:]) @@ -83,8 +82,8 @@ func validateHeaderChainForIngestion(anchor types.BitcoinHeader, headers []types // ensure expectedTarget doesn't change // it's allowed to change if the relay is in testnet mode - if isMainnet && btcspv.ExtractTarget(header.Raw) != expectedTarget { - return types.ErrUnexptectedRetarget(types.DefaultCodespace) + if isMainnet && !btcspv.ExtractTarget(header.Raw).Equal(expectedTarget) { + return types.ErrUnexpectedRetarget(types.DefaultCodespace) } // copy header raw into a bytearray @@ -109,7 +108,7 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in anchor := k.GetHeader(ctx, headers[0].PrevHashLE) - err := validateHeaderChainForIngestion(anchor, headers, internal, k.IsMainNet) + err := validateHeaderChain(anchor, headers, internal, k.IsMainNet) if err != nil { return err } @@ -121,7 +120,7 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in return nil } -func validateDifficultyChangeForIngestion(prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader, prevEpochStart, anchor types.BitcoinHeader) sdk.Error { +func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anchor types.BitcoinHeader) sdk.Error { if anchor.Height%2016 != 2015 { return types.ErrWrongEnd(types.DefaultCodespace) } @@ -159,7 +158,7 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H prevEpochStart := k.GetHeader(ctx, prevEpochStartLE) anchor := k.GetHeader(ctx, headers[0].PrevHashLE) - err := validateDifficultyChangeForIngestion(prevEpochStartLE, headers, prevEpochStart, anchor) + err := validateDifficultyChange(headers, prevEpochStart, anchor) if err != nil { return err } diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go new file mode 100644 index 00000000..6f892132 --- /dev/null +++ b/golang/x/relay/keeper/headers_test.go @@ -0,0 +1,19 @@ +package keeper + +import "log" + +func (suite *KeeperSuite) TestValidateHeaderChain() { + cases := suite.Fixtures.HeaderTestCases.ValidateChain + + for _, tc := range cases { + err := validateHeaderChain(tc.Anchor, tc.Headers, tc.Internal, tc.IsMainnet) + if tc.Output == 0 { + if err != nil { + log.Printf("Unexpected Error\n%s\n", err.Error()) + } + suite.Nil(err) + } else { + suite.Equal(tc.Output, err.Code()) + } + } +} diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 221806f2..30f2f7bf 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -7,19 +7,40 @@ import ( "os" "testing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/suite" + + "github.com/summa-tx/relays/golang/x/relay/types" ) type LinkTest struct{} type ChainTest struct{} -type HeaderTest struct{} +type IngestCase struct { + Headers []types.BitcoinHeader `json:"headers"` + Anchor types.BitcoinHeader `json:"anchor"` + Internal bool `json:"internal"` + IsMainnet bool `json:"isMainnet"` + Output sdk.CodeType `json:"output"` +} + +type DiffChangeCase struct { + Headers []types.BitcoinHeader `json:"headers"` + PrevEpochStart types.BitcoinHeader `json:"prevEpochStart"` + Anchor types.BitcoinHeader `json:"anchor"` + Output string `json:"output"` +} + +type HeaderTestCases struct { + ValidateDiffChange []DiffChangeCase `json:"validateDifficultyChange"` + ValidateChain []IngestCase `json:"validateHeaderChain"` +} type KeeperTestCases struct { - LinkTestCases []LinkTest `json:"link"` - HeaderTestCases []HeaderTest `json:"header"` - ChainTestCases []ChainTest `json:"chain"` + LinkTestCases []LinkTest `json:"link"` + HeaderTestCases HeaderTestCases `json:"header"` + ChainTestCases []ChainTest `json:"chain"` } type KeeperSuite struct { @@ -50,7 +71,3 @@ func TestKeeper(t *testing.T) { suite.Run(t, keeperSuite) } - -func (suite *KeeperSuite) TestGetPrefixStore() { - -} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 31a6bb80..10074318 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -12,148 +12,183 @@ const ( // UnknownError is an unknown error UnknownError sdk.CodeType = 100 + // UnknownErrorMessage is the corresponding message + UnknownErrorMessage = "Unknown Error" // BadHeaderLength means Header array length not divisible by 80 BadHeaderLength sdk.CodeType = 101 + // BadHeaderLengthMessage is the corresponding message + BadHeaderLengthMessage = "Header array length must be divisible by 80" // InsufficientWork means a block did not have sufficient work InsufficientWork sdk.CodeType = 102 + // InsufficientWorkMessage is the corresponding message + InsufficientWorkMessage = "Header work is insufficient" // UnknownBlock is the error code for unknown blocks UnknownBlock sdk.CodeType = 103 + // UnknownBlockMessage is the corresponding message + UnknownBlockMessage = "Unknown block" // BadHeight occurs when a proposed descendant is below a proposed ancestor BadHeight sdk.CodeType = 104 + // BadHeightMessage is the corresponding message + BadHeightMessage = "A descendant height is below the ancestor height" // BadHash256Digest occurs when a wrong-length hash256 digest is found BadHash256Digest sdk.CodeType = 105 + // BadHash256DigestMessage is the corresponding message + BadHash256DigestMessage = "Digest had wrong length" // BadHex occurs when a hex argument couldn't be deserialized BadHex sdk.CodeType = 106 + // BadHexMessage is the corresponding message + BadHexMessage = "Bad hex string in query or msg" // BitcoinSPV is the code for errors bubbled up from Bitcoin SPV BitcoinSPV sdk.CodeType = 107 + // BitcoinSPVMessage is the corresponding message // AlreadyInit is the code for a second attempt to init the relay AlreadyInit sdk.CodeType = 108 + // AlreadyInitMessage is the corresponding message + AlreadyInitMessage = "Relay has already set genesis state" // 200-block -- AddHeaders - // UnexptectedRetarget indicates a retarget was seen during AddHeaders loop - UnexptectedRetarget sdk.CodeType = 201 + // UnexpectedRetarget indicates a retarget was seen during AddHeaders loop + UnexpectedRetarget sdk.CodeType = 201 + // UnexpectedRetargetMessage is the corresponding message + UnexpectedRetargetMessage = "Target changed unexpectedly" - // BadLink indicates a broken link was found in the header array during AddHeaders loop + // BadLink indicates a broken link was found in the header array during IngestHeaders loop BadLink sdk.CodeType = 202 + // BadLinkMessage is the corresponding message + BadLinkMessage = "Headers do not form a consistent chain" // 300-block AddHeadersWithRetarget // WrongEnd means the end block is at the wrong height WrongEnd sdk.CodeType = 301 + // WrongEndMessage is the corresponding message + WrongEndMessage = "Must provide the last header of the closing difficulty period" // WrongStart means the start block is at the wrong height WrongStart sdk.CodeType = 302 + // WrongStartMessage is the corresponding message + WrongStartMessage = "Must provide exactly 1 difficulty period" // PeriodMismatch means the start and end block do not have the same difficulty PeriodMismatch sdk.CodeType = 303 + // PeriodMismatchMessage is the corresponding message + PeriodMismatchMessage = "Period header difficulties do not match" // BadRetarget means the provided blocks did not create the expected retarget BadRetarget sdk.CodeType = 304 + // BadRetargetMessage is the corresponding message + BadRetargetMessage = "Invalid retarget provided" // 400-block -- MarkNewBestHeight // NotBestKnown means a block should have been the best known, but wasn't NotBestKnown sdk.CodeType = 403 + // NotBestKnownMessage is the corresponding message + NotBestKnownMessage = "Provided digest is not current best known" // NotHeaviestAncestor means a later common ancestor was found NotHeaviestAncestor sdk.CodeType = 404 + // NotHeaviestAncestorMessage is the corresponding message + NotHeaviestAncestorMessage = "Ancestor must be heaviest common ancestor" // NotHeavier means the proposed new best is not heavier than the current best NotHeavier sdk.CodeType = 405 + // NotHeavierMessage is the corresponding message + NotHeavierMessage = "New best hash does not have more work than previous" ) // ErrUnknownError throws an error func ErrUnknownError(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnknownError, "Unknown Error") + return sdk.NewError(codespace, UnknownError, UnknownErrorMessage) } // ErrBadHeaderLength throws an error func ErrBadHeaderLength(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHeaderLength, "Header array length must be divisible by 80") + return sdk.NewError(codespace, BadHeaderLength, BadHeaderLengthMessage) } // ErrInsufficientWork throws an error func ErrInsufficientWork(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, InsufficientWork, "Header work is insufficient") + return sdk.NewError(codespace, InsufficientWork, InsufficientWorkMessage) } // ErrUnknownBlock throws an error func ErrUnknownBlock(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnknownBlock, "Unknown block") + return sdk.NewError(codespace, UnknownBlock, UnknownBlockMessage) } // ErrBadHeight throws an error func ErrBadHeight(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHeight, "A descendant height is below the ancestor height") + return sdk.NewError(codespace, BadHeight, BadHeightMessage) } -// ErrUnexptectedRetarget throws an error -func ErrUnexptectedRetarget(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnexptectedRetarget, "Target changed unexpectedly") +// ErrUnexpectedRetarget throws an error +func ErrUnexpectedRetarget(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, UnexpectedRetarget, UnexpectedRetargetMessage) } // ErrBadLink throws an error func ErrBadLink(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadLink, "Headers do not form a consistent chain") + return sdk.NewError(codespace, BadLink, BadLinkMessage) } // ErrWrongEnd throws an error func ErrWrongEnd(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, WrongEnd, "Must provide the last header of the closing difficulty period") + return sdk.NewError(codespace, WrongEnd, WrongEndMessage) } // ErrWrongStart throws an error func ErrWrongStart(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, WrongStart, "Must provide exactly 1 difficulty period") + return sdk.NewError(codespace, WrongStart, WrongStartMessage) } // ErrPeriodMismatch throws an error func ErrPeriodMismatch(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, PeriodMismatch, "Period header difficulties do not match") + return sdk.NewError(codespace, PeriodMismatch, PeriodMismatchMessage) } // ErrBadRetarget throws an error func ErrBadRetarget(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadRetarget, "Invalid retarget provided") + return sdk.NewError(codespace, BadRetarget, BadRetargetMessage) } // ErrNotBestKnown throws an error func ErrNotBestKnown(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotBestKnown, "New best is unknown") + return sdk.NewError(codespace, NotBestKnown, NotBestKnownMessage) } // ErrNotHeaviestAncestor throws an error func ErrNotHeaviestAncestor(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotHeaviestAncestor, "Ancestor must be heaviest common ancestor") + return sdk.NewError(codespace, NotHeaviestAncestor, NotHeaviestAncestorMessage) } // ErrNotHeavier throws an error func ErrNotHeavier(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotHeavier, "New best hash does not have more work than previous") + return sdk.NewError(codespace, NotHeavier, NotHeavierMessage) } // ErrBadHash256Digest throws an error func ErrBadHash256Digest(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHash256Digest, "Digest had wrong length") + return sdk.NewError(codespace, BadHash256Digest, BadHash256DigestMessage) } // ErrBadHex throws an error func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHex, "Bad hex string in query or msg") + return sdk.NewError(codespace, BadHex, BadHexMessage) } // ErrAlreadyInit throws an error func ErrAlreadyInit(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, AlreadyInit, "Relay has already set genesis state") + return sdk.NewError(codespace, AlreadyInit, AlreadyInitMessage) } // FromBTCSPVError converts a btcutils error into an sdk error diff --git a/testVectors.json b/testVectors.json index 1a2ba9f9..8530a6c6 100644 --- a/testVectors.json +++ b/testVectors.json @@ -2,9 +2,205 @@ "link": [ {} ], - "header": [ - {} - ], + "header": { + "validateDifficultyChange": [ + { + "headers": [{}], + "prevEpochStart": {}, + "anchor": {}, + "output": 0 + } + ], + "validateHeaderChain": [ + { + "headers": [ + { + "height": 562623, + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" + }, + { + "height": 562624, + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" + }, + { + "height": 562625, + "hash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", + "hash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "prevhash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "prevhash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "merkle_root": "0x14a87c0e6d701c62e4cb4f3034f55bcbfd670657aa853e9b46ef9524c16582e7", + "merkle_root_le": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814", + "raw": "0x00e0ff3f8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000e78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca8149dc3615c886f2e171c3fa2b8" + }, + { + "height": 562626, + "hash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", + "hash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "prevhash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", + "prevhash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "merkle_root": "0xda33c59bc6f3c950d8bc9be1779b0c03ddc67f7ab9304fb62cd456a1840b516f", + "merkle_root_le": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da", + "raw": "0x00000020fee20039fe494f7408c090e03970ec9b132366066f380d0000000000000000006f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533dacec3615c886f2e170a1a921d" + }, + { + "height": 562627, + "hash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", + "hash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "prevhash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", + "prevhash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "merkle_root": "0x20ead5553be95c71e87839492197ea48ccf87dbeb0b25ed4bc4e97bb0214a36a", + "merkle_root_le": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20", + "raw": "0x00000020d2406bb15e4f917104d2b2d9320454b947fb08a723301d0000000000000000006aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20d1c5615c886f2e1767973d73" + }, + { + "height": 562628, + "hash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", + "hash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "prevhash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", + "prevhash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "merkle_root": "0xe9ab8cb51b64c924b61c52f8be389955c983ae2315ee042dc76cab3b3a338824", + "merkle_root_le": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9", + "raw": "0x00e0002096f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b0000000000000000002488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe953ce615c886f2e172e0885ce" + }, + { + "height": 562629, + "hash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "hash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "prevhash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", + "prevhash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "merkle_root": "0x7f8d7afb20451eb0dfd026ecc2f71e9f126fc536939fe3de6ed324d36f0a2ee7", + "merkle_root_le": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f", + "raw": "0x00000020b86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000e72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7fcdce615c886f2e17415cdb46" + }, + { + "height": 562630, + "hash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", + "hash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0xec8294c76e141d9244c6fec847fea8dbbb529933b279a2102a61da7f5179149d", + "merkle_root_le": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec", + "raw": "0x0000002020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f050000000000000000009d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ecebd3615c886f2e179234c38e" + }, + { + "height": 562631, + "hash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", + "hash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "prevhash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", + "prevhash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "merkle_root": "0x31b36a8983956a484b5ba867fa9cd7e70ae0b4c78dcbe6da1353eb564744af00", + "merkle_root_le": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331", + "raw": "0x0000002051214b0c42383a1ea7bf28f20062f81d7b72497cb1030a00000000000000000000af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab3314bd8615c886f2e17f152bc1f" + }, + { + "height": 562632, + "hash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", + "hash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "prevhash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", + "prevhash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "merkle_root": "0xd029d1fadb3f9e673ff5155d227918c860962dcc1999bb77d6e2d655b5fcc2b2", + "merkle_root_le": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0", + "raw": "0x00e00020bf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000b2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d032db615c886f2e17155f22df" + }, + { + "height": 562633, + "hash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", + "hash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "prevhash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", + "prevhash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "merkle_root": "0x351f74fe8fab94e161ce4f9d2f2da2cac1b7af93abe7054a1d37635f5cf7334e", + "merkle_root_le": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35", + "raw": "0x00000020a4c292016c1585e6f81986f7e216c79d28b15b1d513a100000000000000000004e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35c0de615c886f2e17032ddf4b" + }, + { + "height": 562634, + "hash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", + "hash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "prevhash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", + "prevhash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "merkle_root": "0x80214eef007bb437affecc7279cd81fa215e40096f47622d90e19ab0520d20ef", + "merkle_root_le": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180", + "raw": "0x00000020577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000ef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180cdde615c886f2e171f2b5f80" + }, + { + "height": 562635, + "hash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", + "hash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "prevhash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", + "prevhash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "merkle_root": "0x9c58834eb6b069f57886efe06fe2af4be50cf82446160609e728867bfc3817a9", + "merkle_root_le": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c", + "raw": "0x00000020f5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000a91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589cb3e0615c886f2e17c624e58c" + }, + { + "height": 562636, + "hash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", + "hash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "prevhash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", + "prevhash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "merkle_root": "0xc2c7bcd4be5c099df57632870ccf7cc345df60d46576197321ac90fef0149bc2", + "merkle_root_le": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2", + "raw": "0x00e00020f98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000c29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2fae4615c886f2e178a505d9d" + }, + { + "height": 562637, + "hash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", + "hash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "prevhash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", + "prevhash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "merkle_root": "0x650d7ffc89ce1160ed30f83a2c0bbe807f289987076e19f1916f861929700535", + "merkle_root_le": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65", + "raw": "0x000000200ed18ffcb751e45471dddab23d34538869d3b2cdd484280000000000000000003505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d657ce5615c886f2e17d759c936" + }, + { + "height": 562638, + "hash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", + "hash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "prevhash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", + "prevhash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "merkle_root": "0x8258a466e5628d96ced147e5e18955923699b2daad38be299e42490880327a09", + "merkle_root_le": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882", + "raw": "0x0000802030ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882f1e5615c886f2e1796e65983" + }, + { + "height": 562639, + "hash": "0x0000000000000000000fb8dba7f48eb65dbc24c6dc03d6f84823b5d59c7c4148", + "hash_le": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", + "prevhash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", + "prevhash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "merkle_root": "0xe9bedf6cd3fe3df1a7ba1d194d42a5caca6a3b323ed28270c2db2ff72d698818", + "merkle_root_le": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9", + "raw": "0x0000802051f32093b60f4484c041d383fb6bf35c44d428bde54d2d0000000000000000001888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee905e6615c886f2e177b59d28f" + } + ], + "anchor": { + "height": 562622, + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" + }, + "internal": false, + "isMainnet": true, + "output": 0 + } + ] + }, "chain": [ {} ] From 24c276966a3ddd5780d3c367f1a526ce36cefb04 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sat, 30 Nov 2019 15:53:14 -0800 Subject: [PATCH 041/148] tests: first test for validateDifficultyChange --- golang/x/relay/keeper/headers.go | 2 +- golang/x/relay/keeper/headers_test.go | 17 ++++++++++ golang/x/relay/keeper/keeper_test.go | 2 +- testVectors.json | 45 +++++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index f28361ea..967743e2 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -127,7 +127,7 @@ func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anc if anchor.Height != prevEpochStart.Height+2015 || anchor.Height < prevEpochStart.Height { return types.ErrWrongStart(types.DefaultCodespace) } - if btcspv.ExtractDifficulty(anchor.Raw) != btcspv.ExtractDifficulty(prevEpochStart.Raw) { + if !btcspv.ExtractDifficulty(anchor.Raw).Equal(btcspv.ExtractDifficulty(prevEpochStart.Raw)) { return types.ErrPeriodMismatch(types.DefaultCodespace) } diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 6f892132..54843d5c 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -17,3 +17,20 @@ func (suite *KeeperSuite) TestValidateHeaderChain() { } } } + +func (suite *KeeperSuite) TestValidateDifficultyChange() { + cases := suite.Fixtures.HeaderTestCases.ValidateDiffChange + + for _, tc := range cases { + err := validateDifficultyChange(tc.Headers, tc.PrevEpochStart, tc.Anchor) + if tc.Output == 0 { + if err != nil { + log.Printf("Unexpected Error\n%s\n", err.Error()) + } + suite.Nil(err) + } else { + suite.Equal(tc.Output, err.Code()) + } + } + +} diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 30f2f7bf..ddc862bd 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -29,7 +29,7 @@ type DiffChangeCase struct { Headers []types.BitcoinHeader `json:"headers"` PrevEpochStart types.BitcoinHeader `json:"prevEpochStart"` Anchor types.BitcoinHeader `json:"anchor"` - Output string `json:"output"` + Output sdk.CodeType `json:"output"` } type HeaderTestCases struct { diff --git a/testVectors.json b/testVectors.json index 8530a6c6..69139426 100644 --- a/testVectors.json +++ b/testVectors.json @@ -5,9 +5,48 @@ "header": { "validateDifficultyChange": [ { - "headers": [{}], - "prevEpochStart": {}, - "anchor": {}, + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + }, + { + "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", + "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", + "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "height": 306433, + "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", + "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + } + ], + "prevEpochStart": { + "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", + "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", + "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "height": 304416, + "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", + "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", + "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + }, + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, "output": 0 } ], From 8ef80665b2b28311f8948c8a3f210683e875de49 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sat, 30 Nov 2019 16:31:12 -0800 Subject: [PATCH 042/148] tests: add test case comments and the NamedCase interface --- golang/x/relay/keeper/headers_test.go | 9 ++------- golang/x/relay/keeper/keeper_test.go | 27 ++++++++++++++++++++++++--- testVectors.json | 2 ++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 54843d5c..1f2587e7 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -1,7 +1,5 @@ package keeper -import "log" - func (suite *KeeperSuite) TestValidateHeaderChain() { cases := suite.Fixtures.HeaderTestCases.ValidateChain @@ -9,7 +7,7 @@ func (suite *KeeperSuite) TestValidateHeaderChain() { err := validateHeaderChain(tc.Anchor, tc.Headers, tc.Internal, tc.IsMainnet) if tc.Output == 0 { if err != nil { - log.Printf("Unexpected Error\n%s\n", err.Error()) + logIfTestCaseError(tc, err) } suite.Nil(err) } else { @@ -24,13 +22,10 @@ func (suite *KeeperSuite) TestValidateDifficultyChange() { for _, tc := range cases { err := validateDifficultyChange(tc.Headers, tc.PrevEpochStart, tc.Anchor) if tc.Output == 0 { - if err != nil { - log.Printf("Unexpected Error\n%s\n", err.Error()) - } + logIfTestCaseError(tc, err) suite.Nil(err) } else { suite.Equal(tc.Output, err.Code()) } } - } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index ddc862bd..950b189a 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -13,11 +13,21 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) +type NamedCase interface { + Name() string +} + +type Case struct { + NamedCase + Comment string `json:"comment"` +} + type LinkTest struct{} type ChainTest struct{} type IngestCase struct { + Case Headers []types.BitcoinHeader `json:"headers"` Anchor types.BitcoinHeader `json:"anchor"` Internal bool `json:"internal"` @@ -26,6 +36,7 @@ type IngestCase struct { } type DiffChangeCase struct { + Case Headers []types.BitcoinHeader `json:"headers"` PrevEpochStart types.BitcoinHeader `json:"prevEpochStart"` Anchor types.BitcoinHeader `json:"anchor"` @@ -48,20 +59,30 @@ type KeeperSuite struct { Fixtures KeeperTestCases } -func logIfErr(err error) { +func (c Case) Name() string { + return c.Comment +} + +func logIfError(err error) { if err != nil { log.Fatal(err) } } +func logIfTestCaseError(tc NamedCase, err sdk.Error) { + if err != nil { + log.Printf("Unexpected Error\nIn case: %s\n%s\n", tc.Name(), err.Error()) + } +} + // Runs the whole test suite func TestKeeper(t *testing.T) { jsonFile, err := os.Open("../../../../testVectors.json") defer jsonFile.Close() - logIfErr(err) + logIfError(err) byteValue, err := ioutil.ReadAll(jsonFile) - logIfErr(err) + logIfError(err) var fixtures KeeperTestCases json.Unmarshal([]byte(byteValue), &fixtures) diff --git a/testVectors.json b/testVectors.json index 69139426..397a6655 100644 --- a/testVectors.json +++ b/testVectors.json @@ -5,6 +5,7 @@ "header": { "validateDifficultyChange": [ { + "comment": "valid case", "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", @@ -52,6 +53,7 @@ ], "validateHeaderChain": [ { + "comment": "valid case", "headers": [ { "height": 562623, From 1e1b559015c1d16fbf5717829883c64b028591e4 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Sat, 30 Nov 2019 17:13:09 -0800 Subject: [PATCH 043/148] tests: suite no longer panics if expected error doesn't exist, many more tests --- golang/go.mod | 2 +- golang/go.sum | 2 + golang/x/relay/keeper/headers.go | 1 + golang/x/relay/keeper/headers_test.go | 15 +- golang/x/relay/keeper/keeper_test.go | 11 +- testVectors.json | 255 ++++++++++++++++++++++++++ 6 files changed, 281 insertions(+), 5 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index 3671ab9a..9b366bf6 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -7,7 +7,7 @@ require ( github.com/gorilla/mux v1.7.0 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.3.2 - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.4.0 github.com/summa-tx/bitcoin-spv/golang v1.2.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 diff --git a/golang/go.sum b/golang/go.sum index d88e6ea4..88fbe7d7 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -177,6 +177,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/summa-tx/bitcoin-spv v2.0.0+incompatible h1:so6fH4hvyZ7LI8mC9mLOiV5u+8gxFpsXG84g1dGyK7M= github.com/summa-tx/bitcoin-spv/golang v1.1.0 h1:fpnC/LvOtMFCLrzjCmoTZyCGZr1/skgkJromSk4nBBU= github.com/summa-tx/bitcoin-spv/golang v1.1.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 967743e2..563e56c6 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -36,6 +36,7 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.B // compareTargets compares Bitcoin truncated and full-length targets func compareTargets(full, truncated sdk.Uint) bool { // dirty hacks. sdk.Uint doesn't give us easy access to the underlying + // will be fixed in future sdk version f, _ := full.MarshalAmino() t, _ := truncated.MarshalAmino() fullBI := new(big.Int) diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 1f2587e7..9dd725fe 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -6,11 +6,10 @@ func (suite *KeeperSuite) TestValidateHeaderChain() { for _, tc := range cases { err := validateHeaderChain(tc.Anchor, tc.Headers, tc.Internal, tc.IsMainnet) if tc.Output == 0 { - if err != nil { - logIfTestCaseError(tc, err) - } + logIfTestCaseError(tc, err) suite.Nil(err) } else { + suite.NotNil(err) suite.Equal(tc.Output, err.Code()) } } @@ -25,7 +24,17 @@ func (suite *KeeperSuite) TestValidateDifficultyChange() { logIfTestCaseError(tc, err) suite.Nil(err) } else { + suite.NotNil(err) suite.Equal(tc.Output, err.Code()) } } } + +func (suite *KeeperSuite) TestCompareTargets() { + cases := suite.Fixtures.HeaderTestCases.CompareTargets + + for _, tc := range cases { + result := compareTargets(tc.Full, tc.Truncated) + suite.Equal(result, tc.Output) + } +} diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 950b189a..9bed0901 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -43,9 +43,17 @@ type DiffChangeCase struct { Output sdk.CodeType `json:"output"` } +type CompareCase struct { + Case + Full sdk.Uint `json:"full"` + Truncated sdk.Uint `json:"truncated"` + Output bool `json:"output"` +} + type HeaderTestCases struct { ValidateDiffChange []DiffChangeCase `json:"validateDifficultyChange"` ValidateChain []IngestCase `json:"validateHeaderChain"` + CompareTargets []CompareCase `json:"compareTargets"` } type KeeperTestCases struct { @@ -85,7 +93,8 @@ func TestKeeper(t *testing.T) { logIfError(err) var fixtures KeeperTestCases - json.Unmarshal([]byte(byteValue), &fixtures) + err = json.Unmarshal([]byte(byteValue), &fixtures) + logIfError(err) keeperSuite := new(KeeperSuite) keeperSuite.Fixtures = fixtures diff --git a/testVectors.json b/testVectors.json index 397a6655..d2edc66c 100644 --- a/testVectors.json +++ b/testVectors.json @@ -239,6 +239,261 @@ "internal": false, "isMainnet": true, "output": 0 + }, + { + "comment": "internal, valid", + "headers": [ + { + "height": 562623, + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" + }, + { + "height": 562624, + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" + } + ], + "anchor": { + "height": 562622, + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" + }, + "internal": true, + "isMainnet": true, + "output": 0 + }, + { + "comment": "allowed retarget, because not mainnet", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + }, + { + "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", + "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", + "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "height": 306433, + "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", + "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + } + ], + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "internal": false, + "isMainnet": false, + "output": 0 + }, + { + "comment": "unexpected retarget on non-internal", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + }, + { + "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", + "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", + "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "height": 306433, + "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", + "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + } + ], + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "internal": false, + "isMainnet": true, + "output": 201 + }, + { + "comment": "heights not consecutive", + "headers": [ + { + "height": 562623, + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" + }, + { + "height": 562625, + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" + } + ], + "anchor": { + "height": 562622, + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" + }, + "internal": false, + "isMainnet": true, + "output": 104 + }, + { + "comment": "header doesn't pass validation, header[0].merkle_root_le is wrong", + "headers": [ + { + "height": 562623, + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xF16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" + }, + { + "height": 562624, + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" + } + ], + "anchor": { + "height": 562622, + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" + }, + "internal": false, + "isMainnet": true, + "output": 107 + }, + { + "comment": "header chain doesn't pass validation, anchor is low work", + "headers": [ + { + "height": 562623, + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" + }, + { + "height": 562624, + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" + } + ], + "anchor": { + "height": 562622, + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "raw": "0xF00000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" + }, + "internal": false, + "isMainnet": true, + "output": 107 + } + ], + "compareTargets": [ + { + "full": "0xff", + "truncated": "0xff", + "output": true + }, + { + "full": "0xffff", + "truncated": "0xff00", + "output": true + }, + { + "full": "0xff00", + "truncated": "0xffff", + "output": false + }, + { + "full": "0x1892413990612304162374102234", + "truncated": "0x1892410000000000000000000000", + "output": true + }, + { + "full": "0x0011223344ffeeddccbbaa", + "truncated": "0x000000000000000000", + "output": true } ] }, From b8a1a5e39e46470610db36450c8554c632327617 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 2 Dec 2019 10:57:42 -0800 Subject: [PATCH 044/148] refactor: trim app.go to make a one-module app suitable for integration testing --- golang/Makefile | 24 ++- golang/app.go | 152 +--------------- golang/go.mod | 20 +- golang/go.sum | 261 +++++++++++++++++++++++++++ golang/x/relay/gen_state.go | 16 ++ golang/x/relay/genesis.go | 18 +- golang/x/relay/keeper/chain.go | 38 ++-- golang/x/relay/keeper/headers.go | 30 ++- golang/x/relay/keeper/keeper.go | 29 +++ golang/x/relay/keeper/keeper_test.go | 2 +- golang/x/relay/types/msgs.go | 4 +- 11 files changed, 398 insertions(+), 196 deletions(-) create mode 100644 golang/x/relay/gen_state.go diff --git a/golang/Makefile b/golang/Makefile index e3ed2be3..eaac1b60 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -1,14 +1,30 @@ +PACKAGES=$(shell go list ./... | grep -v '/simulation') + +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') +COMMIT := $(shell git log -1 --format='%H') + +ldflags = -X github.com/cosmos/cosmos-sdk/version.Name=Relay \ + -X github.com/cosmos/cosmos-sdk/version.ServerName=relayd \ + -X github.com/cosmos/cosmos-sdk/version.ClientName=relaycli \ + -X github.com/cosmos/cosmos-sdk/version.Version=$(VERSION) \ + -X github.com/cosmos/cosmos-sdk/version.Commit=$(COMMIT) + +BUILD_FLAGS := -ldflags '$(ldflags)' + all: lint install install: go.sum - go install -mod=readonly $(BUILD_FLAGS) ./cmd/relayd - go install -mod=readonly $(BUILD_FLAGS) ./cmd/relaycli + go install -mod=readonly $(BUILD_FLAGS) ./cmd/relayd + go install -mod=readonly $(BUILD_FLAGS) ./cmd/relaycli go.sum: go.mod - @echo "--> Ensure dependencies have not been modified" - GO111MODULE=on go mod verify + @echo "--> Ensure dependencies have not been modified" + GO111MODULE=on go mod verify lint: golangci-lint run @find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s go mod verify + +test: + @go test -mod=readonly $(PACKAGES) diff --git a/golang/app.go b/golang/app.go index 491d7bbf..74744faf 100644 --- a/golang/app.go +++ b/golang/app.go @@ -7,20 +7,12 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/genaccounts" - "github.com/cosmos/cosmos-sdk/x/genutil" - "github.com/cosmos/cosmos-sdk/x/params" - "github.com/cosmos/cosmos-sdk/x/slashing" - "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" "github.com/tendermint/tendermint/libs/log" "github.com/summa-tx/relays/golang/x/relay" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - distr "github.com/cosmos/cosmos-sdk/x/distribution" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" @@ -38,26 +30,8 @@ var ( // ModuleBasics is basic module elemnets ModuleBasics = module.NewBasicManager( - genaccounts.AppModuleBasic{}, - genutil.AppModuleBasic{}, - auth.AppModuleBasic{}, - bank.AppModuleBasic{}, - staking.AppModuleBasic{}, - distr.AppModuleBasic{}, - params.AppModuleBasic{}, - slashing.AppModuleBasic{}, - supply.AppModuleBasic{}, - relay.AppModule{}, ) - - // account permissions - maccPerms = map[string][]string{ - auth.FeeCollectorName: nil, - distr.ModuleName: nil, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - } ) type relayApp struct { @@ -68,15 +42,6 @@ type relayApp struct { keys map[string]*sdk.KVStoreKey tkeys map[string]*sdk.TransientStoreKey - // Keepers - accountKeeper auth.AccountKeeper - bankKeeper bank.Keeper - stakingKeeper staking.Keeper - slashingKeeper slashing.Keeper - distrKeeper distr.Keeper - supplyKeeper supply.Keeper - paramsKeeper params.Keeper - relayKeeper relay.Keeper // Module Manager @@ -100,16 +65,7 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA // BaseApp handles interactions with Tendermint through the ABCI protocol bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) - keys := sdk.NewKVStoreKeys( - bam.MainStoreKey, - auth.StoreKey, - staking.StoreKey, - supply.StoreKey, - distr.StoreKey, - slashing.StoreKey, - params.StoreKey, - relay.StoreKey) - tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) + keys := sdk.NewKVStoreKeys(relay.StoreKey) // Here you initialize your application with the store keys it requires var app = &relayApp{ @@ -119,77 +75,6 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA tkeys: tkeys, } - // The ParamsKeeper handles parameter storage for the application - app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) - // Set specific supspaces - authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace) - bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace) - stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace) - distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace) - slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace) - - // The AccountKeeper handles address -> account lookups - app.accountKeeper = auth.NewAccountKeeper( - app.cdc, - keys[auth.StoreKey], - authSubspace, - auth.ProtoBaseAccount, - ) - - // The BankKeeper allows you perform sdk.Coins interactions - app.bankKeeper = bank.NewBaseKeeper( - app.accountKeeper, - bankSubspace, - bank.DefaultCodespace, - app.ModuleAccountAddrs(), - ) - - // The SupplyKeeper collects transaction fees and renders them to the fee distribution module - app.supplyKeeper = supply.NewKeeper( - app.cdc, - keys[supply.StoreKey], - app.accountKeeper, - app.bankKeeper, - maccPerms, - ) - - // The staking keeper - stakingKeeper := staking.NewKeeper( - app.cdc, - keys[staking.StoreKey], - tkeys[staking.TStoreKey], - app.supplyKeeper, - stakingSubspace, - staking.DefaultCodespace, - ) - - app.distrKeeper = distr.NewKeeper( - app.cdc, - keys[distr.StoreKey], - distrSubspace, - &stakingKeeper, - app.supplyKeeper, - distr.DefaultCodespace, - auth.FeeCollectorName, - app.ModuleAccountAddrs(), - ) - - app.slashingKeeper = slashing.NewKeeper( - app.cdc, - keys[slashing.StoreKey], - &stakingKeeper, - slashingSubspace, - slashing.DefaultCodespace, - ) - - // register the staking hooks - // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks - app.stakingKeeper = *stakingKeeper.SetHooks( - staking.NewMultiStakingHooks( - app.distrKeeper.Hooks(), - app.slashingKeeper.Hooks()), - ) - // The RelayKeeper is the Keeper from the module for this tutorial // It handles interactions with the store app.relayKeeper = relay.NewKeeper( @@ -199,30 +84,15 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA ) app.mm = module.NewManager( - genaccounts.NewAppModule(app.accountKeeper), - genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx), - auth.NewAppModule(app.accountKeeper), - bank.NewAppModule(app.bankKeeper, app.accountKeeper), relay.NewAppModule(app.relayKeeper), - supply.NewAppModule(app.supplyKeeper, app.accountKeeper), - distr.NewAppModule(app.distrKeeper, app.supplyKeeper), - slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), - staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper), ) - app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) - app.mm.SetOrderEndBlockers(staking.ModuleName) + // app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) + // app.mm.SetOrderEndBlockers(staking.ModuleName) // Sets the order of Genesis - Order matters, genutil is to always come last app.mm.SetOrderInitGenesis( - genaccounts.ModuleName, - distr.ModuleName, - staking.ModuleName, - auth.ModuleName, - bank.ModuleName, - slashing.ModuleName, relay.ModuleName, - genutil.ModuleName, ) // register all module routes and module queriers @@ -233,18 +103,8 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) - // The AnteHandler handles signature verification and transaction pre-processing - app.SetAnteHandler( - auth.NewAnteHandler( - app.accountKeeper, - app.supplyKeeper, - auth.DefaultSigVerificationGasConsumer, - ), - ) - // initialize stores app.MountKVStores(keys) - app.MountTransientStores(tkeys) err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) if err != nil { @@ -288,10 +148,6 @@ func (app *relayApp) LoadHeight(height int64) error { // ModuleAccountAddrs returns all the app's module account addresses. func (app *relayApp) ModuleAccountAddrs() map[string]bool { modAccAddrs := make(map[string]bool) - for acc := range maccPerms { - modAccAddrs[supply.NewModuleAddress(acc).String()] = true - } - return modAccAddrs } @@ -309,7 +165,5 @@ func (app *relayApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLi return nil, nil, err } - validators = staking.WriteValidators(ctx, app.stakingKeeper) - return appState, validators, nil } diff --git a/golang/go.mod b/golang/go.mod index 9b366bf6..a4e44b24 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -3,13 +3,31 @@ module github.com/summa-tx/relays/golang go 1.12 require ( + github.com/bombsimon/wsl v1.2.8 // indirect github.com/cosmos/cosmos-sdk v0.37.0 + github.com/go-critic/go-critic v0.4.0 // indirect + github.com/gogo/protobuf v1.3.1 // indirect + github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect + github.com/golangci/golangci-lint v1.21.0 // indirect + github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect github.com/gorilla/mux v1.7.0 + github.com/gostaticanalysis/analysisutil v0.0.3 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/mattn/go-isatty v0.0.10 // indirect + github.com/pelletier/go-toml v1.6.0 // indirect + github.com/securego/gosec v0.0.0-20191119104125-df484bfa9e9f // indirect + github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cobra v0.0.5 - github.com/spf13/viper v1.3.2 + github.com/spf13/viper v1.5.0 github.com/stretchr/testify v1.4.0 github.com/summa-tx/bitcoin-spv/golang v1.2.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 + github.com/uudashr/gocognit v1.0.0 // indirect + golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect + golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d // indirect + gopkg.in/yaml.v2 v2.2.7 // indirect + mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 // indirect + sourcegraph.com/sqs/pbtypes v1.0.0 // indirect ) diff --git a/golang/go.sum b/golang/go.sum index 88fbe7d7..c58ec2ba 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -2,6 +2,10 @@ bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= +github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -12,8 +16,14 @@ github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d h1:1aAija9gr0Hyv4 github.com/bartekn/go-bip39 v0.0.0-20171116152956-a05967ea095d/go.mod h1:icNx/6QdFblhsEjZehARqbNumymUT/ydwlLojFdv7Sk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs= +github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= +github.com/bombsimon/wsl v1.2.8 h1:b+E/W7koicKBZDU+vEsw/hnQTN8026Gv1eMZDLUU/Wc= +github.com/bombsimon/wsl v1.2.8/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -24,10 +34,14 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa 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/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.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/cosmos-sdk v0.35.0 h1:EPeie1aKHwnXtTzKggvabG7aAPN+DDmju2xquvjFwao= github.com/cosmos/cosmos-sdk v0.35.0/go.mod h1:ruF+G4D7hRf34uzZQvf/SIja9fsIThU5D7GirwTMQ9I= github.com/cosmos/cosmos-sdk v0.37.0 h1:S2I3NDGN2wqfGlY5KqkAHTpfezjhgeqDxrCxhlhd528= @@ -39,52 +53,137 @@ github.com/cosmos/ledger-cosmos-go v0.10.3/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0W github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db h1:GYXWx7Vr3+zv833u+8IoXbNnQY0AdXsxAgI0kX7xcwA= +github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= +github.com/go-critic/go-critic v0.4.0 h1:sXD3pix0wDemuPuSlrXpJNNYXlUiKiysLrtPVQmxkzI= +github.com/go-critic/go-critic v0.4.0/go.mod h1:7/14rZGnZbY6E38VEGk2kVhoq6itzc1E68facVDK23g= github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= +github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= +github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= +github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= +github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= +github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= +github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= +github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= +github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= +github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= +github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= +github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw= +github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129 h1:tT8iWCYw4uOem71yYA3htfH+LNopJvcqZQshm56G5L4= github.com/golang/mock v1.3.1-0.20190508161146-9fa652df1129/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +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/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w= +github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0= +github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= +github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.21.0 h1:HxAxpR8Z0M8omihvQdsD3PF0qPjlqYqp2vMJzstoKeI= +github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI= +github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= +github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 h1:XQKc8IYQOeRwVs36tDrEmTgDgP88d5iEURwpmtiAlOM= +github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= +github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg= +github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +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 v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -94,36 +193,76 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.0.1 h1:9Rrn/H46cXjaA2HQ5Y8lyhOS1NhTkZ4yuEs2r3Eechg= github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE= +github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.6 h1:SrwhHcpV4nWrMGdNcC2kXpMfcBVYGDuTArqyhocJgvA= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= +github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/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= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/otiai10/copy v0.0.0-20180813032824-7e9a647135a1/go.mod h1:pXzZSDlN+HPzSdyIBnKNN9ptD9Hx7iZMWIJPTwo4FPE= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= +github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= 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= @@ -132,29 +271,57 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= +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 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= +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-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137 h1:3l8oligPtjd4JuM+OZ+U8sjtwFGJs98cdWsqs6QZRWs= github.com/prometheus/procfs v0.0.0-20190227231451-bbced9601137/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= +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/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/rakyll/statik v0.1.4/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.5 h1:Ly2UjURzxnsSYS0zI50fZ+srA+Fu7EbpV5hglvJvJG0= github.com/rakyll/statik v0.1.5/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d h1:BzRvVq1EHuIjxpijCEKpAxzKUUMurOQ4sknehIATRh8= +github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= +github.com/securego/gosec v0.0.0-20191119104125-df484bfa9e9f h1:1egnuKwFhkqg8hXU5huGkxz9iPFu4dLINWRSIPjAM+M= +github.com/securego/gosec v0.0.0-20191119104125-df484bfa9e9f/go.mod h1:H5UrtKXL5BGF4FgRa7p2fyqU/lddaTSCLjexYIfS0Bk= +github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= +github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1 h1:qgMbHoJbPbw579P+1zVY+6n4nIFuIchaIjzZ/I/Yq8M= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -167,18 +334,28 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.0.3 h1:z5LPUc2iz8VLT5Cw1UyrESG6FUUnOGecYGY08BLKSuc= github.com/spf13/viper v1.0.3/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.5.0 h1:GpsTwfsQ27oS/Aha/6d1oD7tpKIqWnOA6tgOX9HHkt4= +github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= 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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/summa-tx/bitcoin-spv v2.0.0+incompatible h1:so6fH4hvyZ7LI8mC9mLOiV5u+8gxFpsXG84g1dGyK7M= github.com/summa-tx/bitcoin-spv/golang v1.1.0 h1:fpnC/LvOtMFCLrzjCmoTZyCGZr1/skgkJromSk4nBBU= github.com/summa-tx/bitcoin-spv/golang v1.1.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= @@ -213,12 +390,33 @@ github.com/tendermint/tendermint v0.32.2 h1:FvZWdksfDg/65vKKr5Lgo57keARFnmhrUEXH github.com/tendermint/tendermint v0.32.2/go.mod h1:NwMyx58S8VJ7tEpFKqRVlVWKO9N9zjTHu+Dx96VsnOE= github.com/tendermint/tm-db v0.1.1 h1:G3Xezy3sOk9+ekhjZ/kjArYIs1SmwV+1OUgNkj7RgV0= github.com/tendermint/tm-db v0.1.1/go.mod h1:0cPKWu2Mou3IlxecH+MEUSYc1Ch537alLe6CpFrKzgw= +github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= +github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo= +github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= +github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= +github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517 h1:ChMKTho2hWKpks/nD/FL2KqM1wuVt62oJeiE8+eFpGs= +github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= +github.com/uudashr/gocognit v1.0.0 h1:NST9SQhYHpFiyKdXn8UAiogdB1xkt5RnA1/rR/oBcGw= +github.com/uudashr/gocognit v1.0.0/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -226,19 +424,29 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 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-20180911220305-26e67e76b6c3/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-20181201002055-351d144fa1fc/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-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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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= @@ -248,6 +456,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -255,13 +464,41 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpbl golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/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= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911151314-feee8acb394c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191101200257-8dbcdeb83d3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d h1:/iIZNFGxc/a7C3yWjGcnboV+Tkc7mxr+p6fDztwoxuM= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -271,17 +508,41 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoA google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 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.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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4= +mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= +mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 h1:K7wru2CfJGumS5hkiguQ0Rb9ebKM2Jo8s5d4Jm9lFaM= +mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2/go.mod h1:rCqoQrfAmpTX/h2APczwM7UymU/uvaOluiVPIYCSY/k= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o= +sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= diff --git a/golang/x/relay/gen_state.go b/golang/x/relay/gen_state.go new file mode 100644 index 00000000..25876732 --- /dev/null +++ b/golang/x/relay/gen_state.go @@ -0,0 +1,16 @@ +package relay + +import ( + "encoding/json" +) + +const headerJSON string = "[{\"raw\":\"000000208e244d2c55bc403caa5d6eaf0f922170e413eb1e02fb02000000000000000000e03b4d9df72d8db232a20bb2ff35c433a99f1467f391f75b5f62180d96f06d6aa4c4d65d3eb215179ef91633\",\"hash\":\"00000000000000000012145f8ffa7218d2d04ca66b61835a2a5eaec33dffc098\",\"hash_le\":\"98c0ff3dc3ae5e2a5a83616ba64cd0d21872fa8f5f1412000000000000000000\",\"height\":604800,\"prevhash\":\"00000000000000000002fb021eeb13e47021920faf6e5daa3c40bc552c4d248e\",\"prevhash_le\":\"8e244d2c55bc403caa5d6eaf0f922170e413eb1e02fb02000000000000000000\",\"merkle_root\":\"6a6df0960d18625f5bf791f367149fa933c435ffb20ba232b28d2df79d4d3be0\",\"merkle_root_le\":\"e03b4d9df72d8db232a20bb2ff35c433a99f1467f391f75b5f62180d96f06d6a\"},{\"raw\":\"00e0ff3f4170b40ea2cecf83619c940a31ad58cdcbac7cff509a0f0000000000000000000c241812286f0501002b30a10a554ef292922834e2a7d2bcaec3a50bc6a132d54121e45d3eb215175b183342\",\"hash\":\"00000000000000000004669f8082b56686b42523e58b1a8ce3f14830f38f5148\",\"hash_le\":\"48518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000\",\"height\":606210,\"prevhash\":\"0000000000000000000f9a50ff7caccbcd58ad310a949c6183cfcea20eb47041\",\"prevhash_le\":\"4170b40ea2cecf83619c940a31ad58cdcbac7cff509a0f000000000000000000\",\"merkle_root\":\"d532a1c60ba5c3aebcd2a7e234289292f24e550aa1302b0001056f281218240c\",\"merkle_root_le\":\"0c241812286f0501002b30a10a554ef292922834e2a7d2bcaec3a50bc6a132d5\"},{\"raw\":\"0000002048518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000b6cb41ffd9262fb97f8d0d95bfcbe871994b6fb4f486108055f0cb05255e2b169327e45d3eb21517cca22d03\",\"hash\":\"0000000000000000000aea5de9e6bfe679dc1dfd04588aed76b07366121b5c48\",\"hash_le\":\"485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000\",\"height\":606211,\"prevhash\":\"00000000000000000004669f8082b56686b42523e58b1a8ce3f14830f38f5148\",\"prevhash_le\":\"48518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000\",\"merkle_root\":\"162b5e2505cbf055801086f4b46f4b9971e8cbbf950d8d7fb92f26d9ff41cbb6\",\"merkle_root_le\":\"b6cb41ffd9262fb97f8d0d95bfcbe871994b6fb4f486108055f0cb05255e2b16\"},{\"raw\":\"00000020485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000dd69fee2152de8dcf03130c9f0f7a275a8b1bf505f2f48220ce29f9808c4e3bc562ce45d3eb215177a849ca7\",\"hash\":\"000000000000000000026cc286ad60435e8990a68061c9337488d8a8b9ea6e81\",\"hash_le\":\"816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000\",\"height\":606212,\"prevhash\":\"0000000000000000000aea5de9e6bfe679dc1dfd04588aed76b07366121b5c48\",\"prevhash_le\":\"485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000\",\"merkle_root\":\"bce3c408989fe20c22482f5f50bfb1a875a2f7f0c93031f0dce82d15e2fe69dd\",\"merkle_root_le\":\"dd69fee2152de8dcf03130c9f0f7a275a8b1bf505f2f48220ce29f9808c4e3bc\"},{\"raw\":\"00e00020816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000af66c3078533b17cc844baccc1a0da793fee0cea88e5ce3ab98775ca4de040c6002ee45d3eb2151723106eb5\",\"hash\":\"0000000000000000000d8e1f3505cb971edcd2e8c1d832cd6363f42b8e823f58\",\"hash_le\":\"583f828e2bf46363cd32d8c1e8d2dc1e97cb05351f8e0d000000000000000000\",\"height\":606213,\"prevhash\":\"000000000000000000026cc286ad60435e8990a68061c9337488d8a8b9ea6e81\",\"prevhash_le\":\"816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000\",\"merkle_root\":\"c640e04dca7587b93acee588ea0cee3f79daa0c1ccba44c87cb1338507c366af\",\"merkle_root_le\":\"af66c3078533b17cc844baccc1a0da793fee0cea88e5ce3ab98775ca4de040c6\"}]" + +func getGenesisHeaders() (BitcoinHeader, []BitcoinHeader) { + var genesisHeaders []BitcoinHeader + err := json.Unmarshal([]byte(headerJSON), &genesisHeaders) + if err != nil { + panic("bad json in gen_state") + } + return genesisHeaders[0], genesisHeaders[1:] +} diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index 46407138..d522da3f 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -42,19 +42,25 @@ func ValidateGenesis(data GenesisState) error { return nil } -// DefaultGenesisState makes a default empty genesis state -// TODO: set recent block as default +// DefaultGenesisState sets block 606210 as genesis func DefaultGenesisState() GenesisState { + periodStart, headers := getGenesisHeaders() return GenesisState{ - Headers: []BitcoinHeader{}, - PeriodStart: BitcoinHeader{}, + Headers: headers, + PeriodStart: periodStart, } } // InitGenesis inits the app state based on the genesis state func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate { - keeper.SetGenesisState(ctx, data.Headers[0], data.PeriodStart) - keeper.IngestHeaderChain(ctx, data.Headers[1:]) + err := keeper.SetGenesisState(ctx, data.Headers[0], data.PeriodStart) + if err != nil { + panic("already init!") + } + err = keeper.IngestHeaderChain(ctx, data.Headers[1:]) + if err != nil { + panic("Bad header chain in genesis state!") + } return []abci.ValidatorUpdate{} } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 4cf97417..c55c2389 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -32,29 +32,6 @@ func (k Keeper) setDigestByStoreKey(ctx sdk.Context, key string, digest types.Ha store.Set([]byte(key), digest[:]) } -// SetGenesisState sets the genesis state -func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { - store := k.getChainStore(ctx) - - if store.Has([]byte(types.RelayGenesisStorage)) { - return types.ErrAlreadyInit(types.DefaultCodespace) - } - - k.ingestHeader(ctx, genesis) - k.ingestHeader(ctx, epochStart) - - k.setRelayGenesis(ctx, genesis.HashLE) - k.setBestKnownDigest(ctx, genesis.HashLE) - k.setLastReorgLCA(ctx, genesis.HashLE) - - return nil -} - -// setRelayGenesis sets the first digest in the relay -func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { - k.setDigestByStoreKey(ctx, types.RelayGenesisStorage, relayGenesis) -} - // GetRelayGenesis returns the first digest in the relay func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { return k.getDigestByStoreKey(ctx, types.RelayGenesisStorage) @@ -119,9 +96,18 @@ func (k Keeper) IsMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, righ // HeaviestFromAncestor determines the heavier descendant of a common ancestor func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, newBest types.Hash256Digest, limit uint32) (types.Hash256Digest, sdk.Error) { - ancestorBlock := k.GetHeader(ctx, ancestor) - leftBlock := k.GetHeader(ctx, currentBest) - rightBlock := k.GetHeader(ctx, newBest) + ancestorBlock, err := k.GetHeader(ctx, ancestor) + if err != nil { + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + leftBlock, err := k.GetHeader(ctx, currentBest) + if err != nil { + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + rightBlock, err := k.GetHeader(ctx, newBest) + if err != nil { + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + } if leftBlock.Height < ancestorBlock.Height || rightBlock.Height < ancestorBlock.Height { return types.Hash256Digest{}, types.ErrBadHeight(types.DefaultCodespace) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 563e56c6..a474a925 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -23,14 +23,18 @@ func (k Keeper) HasHeader(ctx sdk.Context, digestLE types.Hash256Digest) bool { } // GetHeader retrieves a header from the store using its LE diges -func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) types.BitcoinHeader { +func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) (types.BitcoinHeader, sdk.Error) { var header types.BitcoinHeader store := k.getHeaderStore(ctx) + if !store.Has(digestLE[:]) { + return types.BitcoinHeader{}, types.ErrUnknownBlock(types.DefaultCodespace) + } + buf := store.Get(digestLE[:]) k.cdc.MustUnmarshalBinaryBare(buf, &header) - return header + return header, nil } // compareTargets compares Bitcoin truncated and full-length targets @@ -107,9 +111,12 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in return types.ErrUnknownBlock(types.DefaultCodespace) } - anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) + if err != nil { + return types.ErrUnknownBlock(types.DefaultCodespace) + } - err := validateHeaderChain(anchor, headers, internal, k.IsMainNet) + err = validateHeaderChain(anchor, headers, internal, k.IsMainNet) if err != nil { return err } @@ -118,6 +125,9 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in k.setLink(ctx, header) k.ingestHeader(ctx, header) } + + k.emitExtension(ctx, anchor, headers[len(headers)-1]) + return nil } @@ -156,10 +166,16 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H } // Find the anchor in our store - prevEpochStart := k.GetHeader(ctx, prevEpochStartLE) - anchor := k.GetHeader(ctx, headers[0].PrevHashLE) + prevEpochStart, err := k.GetHeader(ctx, prevEpochStartLE) + if err != nil { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) + if err != nil { + return types.ErrUnknownBlock(types.DefaultCodespace) + } - err := validateDifficultyChange(headers, prevEpochStart, anchor) + err = validateDifficultyChange(headers, prevEpochStart, anchor) if err != nil { return err } diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index c15f5596..658a59b0 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -4,6 +4,9 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" + + "github.com/summa-tx/relays/golang/x/relay/types" ) // Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine @@ -25,3 +28,29 @@ func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec, mainnet bool) Keeper { func (k Keeper) getPrefixStore(ctx sdk.Context, namespace string) sdk.KVStore { return prefix.NewStore(ctx.KVStore(k.storeKey), []byte(namespace)) } + +func (k Keeper) hasRelayGenesis(ctx sdk.Context) bool { + store := k.getChainStore(ctx) + return store.Has([]byte(types.RelayGenesisStorage)) +} + +// setRelayGenesis sets the first digest in the relay +func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Digest) { + k.setDigestByStoreKey(ctx, types.RelayGenesisStorage, relayGenesis) +} + +// SetGenesisState sets the genesis state +func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { + if k.hasRelayGenesis(ctx) { + return types.ErrAlreadyInit(types.DefaultCodespace) + } + + k.ingestHeader(ctx, genesis) + k.ingestHeader(ctx, epochStart) + + k.setRelayGenesis(ctx, genesis.HashLE) + k.setBestKnownDigest(ctx, genesis.HashLE) + k.setLastReorgLCA(ctx, genesis.HashLE) + + return nil +} diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 9bed0901..207d2d1b 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -86,8 +86,8 @@ func logIfTestCaseError(tc NamedCase, err sdk.Error) { // Runs the whole test suite func TestKeeper(t *testing.T) { jsonFile, err := os.Open("../../../../testVectors.json") - defer jsonFile.Close() logIfError(err) + defer jsonFile.Close() byteValue, err := ioutil.ReadAll(jsonFile) logIfError(err) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 9b0b1261..b2c8f29a 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -74,7 +74,7 @@ func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() - if valid == false || err != nil { + if !valid || err != nil { return FromBTCSPVError(DefaultCodespace, err) } } @@ -113,7 +113,7 @@ func (msg MsgIngestDifficultyChange) Type() string { return "ingest_difficulty_c func (msg MsgIngestDifficultyChange) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() - if valid == false || err != nil { + if !valid || err != nil { return FromBTCSPVError(DefaultCodespace, err) } } From d71619dcabc1c6871b358d5ee7560304daa4b771 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Mon, 2 Dec 2019 15:51:52 -0700 Subject: [PATCH 045/148] Add Golang Queries (#9) * added query stuff for GetRelayGenesis * added most queries * added todo * updated query res structs * added query for IsMostRecentCommonAncestor * deleted unnecessary code * fixed things, added hash256DigestFromHex function * fixed linting errors * added default limit * reverted change to InitGenesis * added DecodeUint32FromPath, updated QueryResIsAncestor type --- golang/x/relay/client/cli/query.go | 3 +- golang/x/relay/keeper/querier.go | 278 ++++++++++++++++++++++++++--- golang/x/relay/module.go | 2 +- golang/x/relay/types/errors.go | 41 +++++ golang/x/relay/types/querier.go | 77 +++++++- 5 files changed, 369 insertions(+), 32 deletions(-) diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index db7c2b24..e1858950 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -7,12 +7,11 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/spf13/cobra" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" ) type QueryResGetParent struct { - Digest btcspv.Hash256Digest `json:"digest"` + Digest types.Hash256Digest `json:"digest"` } func (d *QueryResGetParent) String() string { diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index d25891a2..f8252518 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/hex" + "strconv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,12 +14,62 @@ import ( // QueryIsAncestor is a query string tag for IsAncestor const QueryIsAncestor = "isancestor" +// QueryGetRelayGenesis is a query string tag for GetRelayGenesis +const QueryGetRelayGenesis = "getrelaygenesis" + +// QueryGetLastReorgLCA is a query string tag for GetLastReorgLCA +const QueryGetLastReorgLCA = "getlastreorglca" + +// QueryFindAncestor is a query string tag for FindAncestor +const QueryFindAncestor = "findancestor" + +// QueryHeaviestFromAncestor is a query string tag for HeaviestFromAncestor +const QueryHeaviestFromAncestor = "heaviestfromancestor" + +// QueryIsMostRecentCommonAncestor is a query string tag for IsMostRecentCommonAncestor +const QueryIsMostRecentCommonAncestor = "ismostrecentcommonancestor" + +// hash256DigestFromHex converts a hex into a Hash256Digest +func hash256DigestFromHex(hexStr string) (types.Hash256Digest, sdk.Error) { + bytes, decodeErr := hex.DecodeString(hexStr) + if decodeErr != nil { + return types.Hash256Digest{}, types.ErrBadHex(types.DefaultCodespace) + } + digest, newDigestErr := btcspv.NewHash256Digest(bytes) + if newDigestErr != nil { + return types.Hash256Digest{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) + } + return digest, nil +} + +func DecodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) { + if len(path) >= idx { + return defaultLimit, nil + } + // parse int from path[idx], return error if necessary + num, convErr := strconv.ParseUint(path[idx], 0, 32) + if convErr != nil { + return defaultLimit, types.ErrExternal(types.DefaultCodespace, convErr) + } + return uint32(num), nil +} + // NewQuerier makes a query routing function func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { case QueryIsAncestor: return queryIsAncestor(ctx, path[1:], req, keeper) + case QueryGetRelayGenesis: + return queryGetRelayGenesis(ctx, req, keeper) + case QueryGetLastReorgLCA: + return queryGetLastReorgLCA(ctx, req, keeper) + case QueryFindAncestor: + return queryFindAncestor(ctx, path[1:], req, keeper) + case QueryHeaviestFromAncestor: + return queryHeaviestFromAncestor(ctx, path[1:], req, keeper) + case QueryIsMostRecentCommonAncestor: + return queryIsMostRecentCommonAncestor(ctx, path[1:], req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } @@ -34,48 +85,225 @@ func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keep // getAncestor is removed by the handler switch/case block above - // parse the first path item as hex. - // TODO: check that the path is this many items long, error if not - hexDigest := path[0] - digestBytes, decodeErr := hex.DecodeString(hexDigest) - if decodeErr != nil { - return []byte{}, types.ErrBadHex(types.DefaultCodespace) + // check that the path is this many items long, error if not + if len(path) > 3 { + return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) + } else if len(path) < 2 { + return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) } - digestLE, newDigestErr := btcspv.NewHash256Digest(digestBytes) - if newDigestErr != nil { - return []byte{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) + + // parse the first path item as hex. + digestLE, digestErr := hash256DigestFromHex(path[0]) + if digestErr != nil { + return []byte{}, digestErr } // parse the second path item as hex. - // TODO: check that the path is this many items long, error if not - ancestorDigest := path[1] - ancestorDigestBytes, decodeErr := hex.DecodeString(ancestorDigest) - if decodeErr != nil { - return []byte{}, types.ErrBadHex(types.DefaultCodespace) - } - ancestorDigestLE, newDigestErr := btcspv.NewHash256Digest(ancestorDigestBytes) - if newDigestErr != nil { - return []byte{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) + ancestor, ancestorErr := hash256DigestFromHex(path[1]) + if ancestorErr != nil { + return []byte{}, ancestorErr } - // TODO: check that the path is this many items long, error if not - limit := uint32(25) // TODO: parse from path, use 240 as default if not in path + limit, limitErr := DecodeUint32FromPath(path, 2, 15) + if limitErr != nil { + return []byte{}, limitErr + } // This calls the keeper with the parsed arguments, and gets an answer - result := keeper.IsAncestor(ctx, ancestorDigestLE, digestLE, limit) + result := keeper.IsAncestor(ctx, ancestor, digestLE, uint32(limit)) // Now we format the answer as a response response := types.QueryResIsAncestor{ Digest: digestLE, - ProspectiveAncestor: ancestorDigestLE, - IsAncestor: result, + ProspectiveAncestor: ancestor, + Limit: limit, + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + +func queryGetRelayGenesis(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // This calls the keeper and gets an answer + result, err := keeper.GetRelayGenesis(ctx) + if err != nil { + return []byte{}, err + } + + // Now we format the answer as a response + response := types.QueryResGetRelayGenesis{ + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + +func queryGetLastReorgLCA(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // This calls the keeper and gets an answer + result, err := keeper.GetLastReorgLCA(ctx) + if err != nil { + return []byte{}, err + } + + // Now we format the answer as a response + response := types.QueryResGetLastReorgLCA{ + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + +func queryFindAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // check that the path is this many items long, error if not + if len(path) > 2 { + return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) + } else if len(path) < 2 { + return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) + } + + digestLE, digestErr := hash256DigestFromHex(path[0]) + if digestErr != nil { + return []byte{}, digestErr + } + + offset, convErr := strconv.ParseUint(path[1], 0, 32) + if convErr != nil { + return []byte{}, types.ErrExternal(types.DefaultCodespace, convErr) + } + newOffset := uint32(offset) + + // This calls the keeper with the parsed arguments, and gets an answer + result, err := keeper.FindAncestor(ctx, digestLE, newOffset) + if err != nil { + return []byte{}, err + } + + // Now we format the answer as a response + response := types.QueryResFindAncestor{ + DigestLE: digestLE, + Offset: newOffset, + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + +func queryHeaviestFromAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // check that the path is this many items long, error if not + if len(path) > 4 { + return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) + } else if len(path) < 3 { + return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) + } + + ancestor, ancestorErr := hash256DigestFromHex(path[0]) + if ancestorErr != nil { + return []byte{}, ancestorErr + } + + // parse the second path item as hex. + currentBestDigest, currentBestErr := hash256DigestFromHex(path[1]) + if currentBestErr != nil { + return []byte{}, currentBestErr + } + + newBestDigest, newBestErr := hash256DigestFromHex(path[2]) + if newBestErr != nil { + return []byte{}, newBestErr + } + + limit, limitErr := DecodeUint32FromPath(path, 3, 15) + if limitErr != nil { + return []byte{}, limitErr + } + + // This calls the keeper with the parsed arguments, and gets an answer + result, err := keeper.HeaviestFromAncestor(ctx, ancestor, currentBestDigest, newBestDigest, limit) + if err != nil { + return []byte{}, err + } + + // Now we format the answer as a response + response := types.QueryResHeaviestFromAncestor{ + Ancestor: ancestor, + CurrentBest: currentBestDigest, + NewBest: newBestDigest, + Limit: limit, + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + +func queryIsMostRecentCommonAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // check that the path is this many items long, error if not + if len(path) > 4 { + return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) + } else if len(path) < 3 { + return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) + } + + ancestor, ancestorErr := hash256DigestFromHex(path[0]) + if ancestorErr != nil { + return []byte{}, ancestorErr + } + + leftDigest, leftErr := hash256DigestFromHex(path[1]) + if leftErr != nil { + return []byte{}, leftErr + } + + rightDigest, rightErr := hash256DigestFromHex(path[2]) + if rightErr != nil { + return []byte{}, rightErr + } + + limit, limitErr := DecodeUint32FromPath(path, 3, 15) + if limitErr != nil { + return []byte{}, limitErr + } + + // This calls the keeper with the parsed arguments, and gets an answer + result := keeper.IsMostRecentCommonAncestor(ctx, ancestor, leftDigest, rightDigest, limit) + + // Now we format the answer as a response + response := types.QueryResIsMostRecentCommonAncestor{ + Ancestor: ancestor, + Left: leftDigest, + Right: rightDigest, + Limit: limit, + Res: result, } // And we serialize that response as JSON res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) if marshalErr != nil { - // TODO: better handling - panic("could not marshal result to JSON") + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) } return res, nil } diff --git a/golang/x/relay/module.go b/golang/x/relay/module.go index 2de6388f..5bbc74b9 100644 --- a/golang/x/relay/module.go +++ b/golang/x/relay/module.go @@ -71,7 +71,7 @@ func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { // AppModule is the AppModule type AppModule struct { AppModuleBasic - keeper Keeper + keeper Keeper } // NewAppModule creates a new AppModule Object diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 10074318..076529e5 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -104,6 +104,27 @@ const ( NotHeavier sdk.CodeType = 405 // NotHeavierMessage is the corresponding message NotHeavierMessage = "New best hash does not have more work than previous" + + // 500-block Queries + + // NotEnoughArguments means there are not enough arguments specified in the path of a query + NotEnoughArguments sdk.CodeType = 501 + // NotEnoughArgumentsMessage is the corresponding message + NotEnoughArgumentsMessage = "Not enough arguments" + + // TooManyArguments means there are too many arguments specified in the path of a query + TooManyArguments sdk.CodeType = 502 + // TooManyArgumentsMessage is the corresponding message + TooManyArgumentsMessage = "Too many arguments" + + // MarshalJSON means there was an error marshalling a query result to json + MarshalJSON sdk.CodeType = 503 + // MarshalJSONMessage is the corresponding message + MarshalJSONMessage = "Could not marshal result to JSON" + + // 600-block External + + ExternalError sdk.CodeType = 601 ) // ErrUnknownError throws an error @@ -191,7 +212,27 @@ func ErrAlreadyInit(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, AlreadyInit, AlreadyInitMessage) } +// ErrTooManyArguments throws an error +func ErrTooManyArguments(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, TooManyArguments, TooManyArgumentsMessage) +} + +// ErrNotEnoughArguments throws an error +func ErrNotEnoughArguments(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotEnoughArguments, NotEnoughArgumentsMessage) +} + +// ErrMarshalJSON throws an error +func ErrMarshalJSON(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, MarshalJSON, MarshalJSONMessage) +} + // FromBTCSPVError converts a btcutils error into an sdk error func FromBTCSPVError(codespace sdk.CodespaceType, err error) sdk.Error { return sdk.NewError(codespace, BitcoinSPV, err.Error()) } + +// ErrExternal converts any external error into an sdk error +func ErrExternal(codespace sdk.CodespaceType, err error) sdk.Error { + return sdk.NewError(codespace, ExternalError, err.Error()) +} diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index b3b788ce..b55bdd5c 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -3,18 +3,87 @@ package types import ( "encoding/hex" "fmt" - "strings" ) type QueryResIsAncestor struct { Digest Hash256Digest `json:"digest"` ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` - IsAncestor bool `json:"isAncestor"` + Limit uint32 `json:"limit"` + Res bool `json:"result"` } func (r QueryResIsAncestor) String() string { dig := "0x" + hex.EncodeToString(r.Digest[:]) digAnc := "0x" + hex.EncodeToString(r.ProspectiveAncestor[:]) - res := fmt.Sprintf("%t\n", r.IsAncestor) - return strings.Join([]string{dig, digAnc, res}, "\n") + return fmt.Sprintf( + "Digest: %s, Ancestor: %s, Limit: %d, Result: %t", + dig, digAnc, r.Limit, r.Res) +} + +type QueryResGetRelayGenesis struct { + Res Hash256Digest `json:"result"` +} + +func (r QueryResGetRelayGenesis) String() string { + digest := "0x" + hex.EncodeToString(r.Res[:]) + return fmt.Sprintf("%s\n", digest) +} + +type QueryResGetLastReorgLCA struct { + Res Hash256Digest `json:"result"` +} + +func (r QueryResGetLastReorgLCA) String() string { + digest := "0x" + hex.EncodeToString(r.Res[:]) + return fmt.Sprintf("%s\n", digest) +} + +type QueryResFindAncestor struct { + DigestLE Hash256Digest `json:"digestLE"` + Offset uint32 `json:"offset"` + Res Hash256Digest `json:"result"` +} + +func (r QueryResFindAncestor) String() string { + dig := "0x" + hex.EncodeToString(r.DigestLE[:]) + offset := r.Offset + res := "0x" + hex.EncodeToString(r.Res[:]) + return fmt.Sprintf( + "Digest LE: %s, Offset: %d, Result: %s", + dig, offset, res) +} + +type QueryResHeaviestFromAncestor struct { + Ancestor Hash256Digest `json:"ancestor"` + CurrentBest Hash256Digest `json:"currentBest"` + NewBest Hash256Digest `json:"newBest"` + Limit uint32 `json:"limit"` + Res Hash256Digest `json:"result"` +} + +func (r QueryResHeaviestFromAncestor) String() string { + anc := "0x" + hex.EncodeToString(r.Ancestor[:]) + curBest := "0x" + hex.EncodeToString(r.CurrentBest[:]) + newBest := "0x" + hex.EncodeToString(r.NewBest[:]) + res := "0x" + hex.EncodeToString(r.Res[:]) + return fmt.Sprintf( + "Ancestor: %s, Current Best: %s, New Best: %s, Limit: %d, Result: %s", + anc, curBest, newBest, r.Limit, res) +} + +type QueryResIsMostRecentCommonAncestor struct { + Ancestor Hash256Digest `json:"ancestor"` + Left Hash256Digest `json:"left"` + Right Hash256Digest `json:"right"` + Limit uint32 `json:"limit"` + Res bool `json:"result"` +} + +func (r QueryResIsMostRecentCommonAncestor) String() string { + anc := "0x" + hex.EncodeToString(r.Ancestor[:]) + left := "0x" + hex.EncodeToString(r.Left[:]) + right := "0x" + hex.EncodeToString(r.Right[:]) + return fmt.Sprintf( + "Ancestor: %s, Left: %s, Right: %s, Limit: %d, Result: %t", + anc, left, right, r.Limit, r.Res) } From bd4d0102bed92ece3d07c19a31a775d694d41071 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Tue, 3 Dec 2019 12:03:45 -0700 Subject: [PATCH 046/148] added tests, added docs in types/querier --- golang/x/relay/keeper/querier.go | 10 +-- golang/x/relay/keeper/querier_test.go | 92 +++++++++++++++++++++++++++ golang/x/relay/types/querier.go | 12 ++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 golang/x/relay/keeper/querier_test.go diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index f8252518..0eb1d88e 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -42,8 +42,8 @@ func hash256DigestFromHex(hexStr string) (types.Hash256Digest, sdk.Error) { return digest, nil } -func DecodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) { - if len(path) >= idx { +func decodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) { + if idx+1 > len(path) { return defaultLimit, nil } // parse int from path[idx], return error if necessary @@ -104,7 +104,7 @@ func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keep return []byte{}, ancestorErr } - limit, limitErr := DecodeUint32FromPath(path, 2, 15) + limit, limitErr := decodeUint32FromPath(path, 2, 15) if limitErr != nil { return []byte{}, limitErr } @@ -232,7 +232,7 @@ func queryHeaviestFromAncestor(ctx sdk.Context, path []string, req abci.RequestQ return []byte{}, newBestErr } - limit, limitErr := DecodeUint32FromPath(path, 3, 15) + limit, limitErr := decodeUint32FromPath(path, 3, 15) if limitErr != nil { return []byte{}, limitErr } @@ -283,7 +283,7 @@ func queryIsMostRecentCommonAncestor(ctx sdk.Context, path []string, req abci.Re return []byte{}, rightErr } - limit, limitErr := DecodeUint32FromPath(path, 3, 15) + limit, limitErr := decodeUint32FromPath(path, 3, 15) if limitErr != nil { return []byte{}, limitErr } diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go new file mode 100644 index 00000000..252e71b0 --- /dev/null +++ b/golang/x/relay/keeper/querier_test.go @@ -0,0 +1,92 @@ +package keeper + +import ( + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func (suite *KeeperSuite) TestHash256DigestFromHex() { + Hash256FromHexPass := []struct { + Input string + Output types.Hash256Digest + }{ + { + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + types.Hash256Digest{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + } + + Hash256FromHexFail := []struct { + Input string + Err string + }{ + { + "jjjjjj", + "ERROR:\nCodespace: relay\nCode: 106\nMessage: \"Bad hex string in query or msg\"\n", + }, { + "ffffff", + "ERROR:\nCodespace: relay\nCode: 107\nMessage: \"Expected 32 bytes in a Hash256Digest, got 3\"\n", + }, + } + + for i := range Hash256FromHexPass { + digest, err := hash256DigestFromHex(Hash256FromHexPass[i].Input) + suite.Nil(err) + suite.Equal(digest, Hash256FromHexPass[i].Output) + } + for i := range Hash256FromHexFail { + _, err := hash256DigestFromHex(Hash256FromHexFail[i].Input) + suite.EqualError(err, Hash256FromHexFail[i].Err) + } +} + +func (suite *KeeperSuite) TestDecodeUint32FromPath() { + // (path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) + DecodeUintPass := []struct { + Path []string + Idx int + DefaultLimit uint32 + Output uint32 + }{ + { + []string{"", "", "12"}, + 2, + 15, + 12, + }, { + []string{"", ""}, + 2, + 15, + 15, + }, + } + + DecodeUintFail := []struct { + Path []string + Idx int + DefaultLimit uint32 + Err string + }{ + { + []string{"", "", "aj"}, + 2, + 15, + "ERROR:\nCodespace: relay\nCode: 601\nMessage: \"strconv.ParseUint: parsing \\\"aj\\\": invalid syntax\"\n", + }, + } + + for i := range DecodeUintPass { + path := DecodeUintPass[i].Path + index := DecodeUintPass[i].Idx + limit := DecodeUintPass[i].DefaultLimit + num, err := decodeUint32FromPath(path, index, limit) + suite.Nil(err) + suite.Equal(num, DecodeUintPass[i].Output) + } + for i := range DecodeUintFail { + path := DecodeUintFail[i].Path + index := DecodeUintFail[i].Idx + limit := DecodeUintFail[i].DefaultLimit + _, err := decodeUint32FromPath(path, index, limit) + suite.EqualError(err, DecodeUintFail[i].Err) + } +} diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index b55bdd5c..0bb4a781 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -5,6 +5,7 @@ import ( "fmt" ) +// QueryResIsAncestor is the response struct for queryIsAncestor type QueryResIsAncestor struct { Digest Hash256Digest `json:"digest"` ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` @@ -12,6 +13,7 @@ type QueryResIsAncestor struct { Res bool `json:"result"` } +// String formats a QueryResIsAncestor struct func (r QueryResIsAncestor) String() string { dig := "0x" + hex.EncodeToString(r.Digest[:]) digAnc := "0x" + hex.EncodeToString(r.ProspectiveAncestor[:]) @@ -20,30 +22,36 @@ func (r QueryResIsAncestor) String() string { dig, digAnc, r.Limit, r.Res) } +// QueryResGetRelayGenesis is the response struct for queryGetRelayGenesis type QueryResGetRelayGenesis struct { Res Hash256Digest `json:"result"` } +// String formats a QueryResGetRelayGenesis struct func (r QueryResGetRelayGenesis) String() string { digest := "0x" + hex.EncodeToString(r.Res[:]) return fmt.Sprintf("%s\n", digest) } +// QueryResGetLastReorgLCA is the response struct for queryGetLastReorgLCA type QueryResGetLastReorgLCA struct { Res Hash256Digest `json:"result"` } +// String formats a QueryResGetLastReorgLCA struct func (r QueryResGetLastReorgLCA) String() string { digest := "0x" + hex.EncodeToString(r.Res[:]) return fmt.Sprintf("%s\n", digest) } +// QueryResFindAncestor is the response struct for queryFindAncestor type QueryResFindAncestor struct { DigestLE Hash256Digest `json:"digestLE"` Offset uint32 `json:"offset"` Res Hash256Digest `json:"result"` } +// String formats a QueryResFindAncestor struct func (r QueryResFindAncestor) String() string { dig := "0x" + hex.EncodeToString(r.DigestLE[:]) offset := r.Offset @@ -53,6 +61,7 @@ func (r QueryResFindAncestor) String() string { dig, offset, res) } +// QueryResHeaviestFromAncestor is the response struct for queryHeaviestFromAncestor type QueryResHeaviestFromAncestor struct { Ancestor Hash256Digest `json:"ancestor"` CurrentBest Hash256Digest `json:"currentBest"` @@ -61,6 +70,7 @@ type QueryResHeaviestFromAncestor struct { Res Hash256Digest `json:"result"` } +// String formats a QueryResHeaviestFromAncestor struct func (r QueryResHeaviestFromAncestor) String() string { anc := "0x" + hex.EncodeToString(r.Ancestor[:]) curBest := "0x" + hex.EncodeToString(r.CurrentBest[:]) @@ -71,6 +81,7 @@ func (r QueryResHeaviestFromAncestor) String() string { anc, curBest, newBest, r.Limit, res) } +// QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor type QueryResIsMostRecentCommonAncestor struct { Ancestor Hash256Digest `json:"ancestor"` Left Hash256Digest `json:"left"` @@ -79,6 +90,7 @@ type QueryResIsMostRecentCommonAncestor struct { Res bool `json:"result"` } +// String formats a QueryResIsMostRecentCommonAncestor struct func (r QueryResIsMostRecentCommonAncestor) String() string { anc := "0x" + hex.EncodeToString(r.Ancestor[:]) left := "0x" + hex.EncodeToString(r.Left[:]) From 03e5eaae25d7d4fd2fdec6aee6f2352c93f20222 Mon Sep 17 00:00:00 2001 From: Erin Hales Date: Tue, 3 Dec 2019 12:25:15 -0700 Subject: [PATCH 047/148] updated error comparisons --- golang/x/relay/keeper/querier_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index 252e71b0..d2c53eed 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -1,6 +1,7 @@ package keeper import ( + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -17,14 +18,14 @@ func (suite *KeeperSuite) TestHash256DigestFromHex() { Hash256FromHexFail := []struct { Input string - Err string + Err sdk.CodeType }{ { "jjjjjj", - "ERROR:\nCodespace: relay\nCode: 106\nMessage: \"Bad hex string in query or msg\"\n", + types.BadHex, }, { "ffffff", - "ERROR:\nCodespace: relay\nCode: 107\nMessage: \"Expected 32 bytes in a Hash256Digest, got 3\"\n", + types.BitcoinSPV, }, } @@ -35,12 +36,11 @@ func (suite *KeeperSuite) TestHash256DigestFromHex() { } for i := range Hash256FromHexFail { _, err := hash256DigestFromHex(Hash256FromHexFail[i].Input) - suite.EqualError(err, Hash256FromHexFail[i].Err) + suite.Equal(Hash256FromHexFail[i].Err, err.Code()) } } func (suite *KeeperSuite) TestDecodeUint32FromPath() { - // (path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) DecodeUintPass := []struct { Path []string Idx int @@ -64,13 +64,13 @@ func (suite *KeeperSuite) TestDecodeUint32FromPath() { Path []string Idx int DefaultLimit uint32 - Err string + Err sdk.CodeType }{ { []string{"", "", "aj"}, 2, 15, - "ERROR:\nCodespace: relay\nCode: 601\nMessage: \"strconv.ParseUint: parsing \\\"aj\\\": invalid syntax\"\n", + types.ExternalError, }, } @@ -87,6 +87,6 @@ func (suite *KeeperSuite) TestDecodeUint32FromPath() { index := DecodeUintFail[i].Idx limit := DecodeUintFail[i].DefaultLimit _, err := decodeUint32FromPath(path, index, limit) - suite.EqualError(err, DecodeUintFail[i].Err) + suite.Equal(DecodeUintFail[i].Err, err.Code()) } } From 5155b5feee30c240624b1efec92925979706f195 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Thu, 5 Dec 2019 10:41:17 -0800 Subject: [PATCH 048/148] Cli rest queries (#12) * added query stuff for GetRelayGenesis * added most queries * added todo * updated query res structs * added query for IsMostRecentCommonAncestor * deleted unnecessary code * fixed things, added hash256DigestFromHex function * fixed linting errors * added default limit * reverted change to InitGenesis * refactor: start using req.Data in queries. implement one query in Rest and in CLI * Restructure queries (#13) * restructured queryFindAncestor * added cli cmd function for FindAncestor * fixed typos in cli * added findAncestorHandler * changed message for GetCmdFindAncestor * bug: add FindAncestorHandler to REST router * bug: wrong handler * Updated Query for HeaviestFromAncestor (#14) * updated queryHeaviestFromAncestor * changed heaviestFromAncestor query * added limit check * fixed copy/paste error, updated handleFuncs * Changed isMostRecentCommonAncestor query (#15) * changed isMostRecentCA query * updated router function for isMostRecentCommonAncestor * small bug fixes * fixed merge conflict, fixed merge error, removed comments * missing query route * Add fake app to suite object for testings (#16) * feature: fake_app.go * test: KeeperSuite now has a fake app with persistent state attached * tests: test SetGenesisState * tests: move test app init to TestSetup() * Finish updating queries (#17) * added getRelayGenesis and getLastReorgLCA * removed params section from comments --- golang/.gitignore | 1 + golang/cmd/relayd/main.go | 2 +- golang/{app.go => fake_app.go} | 154 ++++++++++++- golang/go.mod | 2 +- golang/x/relay/client/cli/query.go | 301 ++++++++++++++++++++++++-- golang/x/relay/client/rest/query.go | 243 ++++++++++++++++++++- golang/x/relay/client/rest/rest.go | 22 +- golang/x/relay/client/rest/tx.go | 81 +++---- golang/x/relay/keeper/chain.go | 5 - golang/x/relay/keeper/headers_test.go | 26 +-- golang/x/relay/keeper/keeper.go | 5 + golang/x/relay/keeper/keeper_test.go | 81 +++++++ golang/x/relay/keeper/links.go | 6 +- golang/x/relay/keeper/querier.go | 207 +++++------------- golang/x/relay/keeper/querier_test.go | 35 --- golang/x/relay/types/errors.go | 1 + golang/x/relay/types/querier.go | 93 +++++--- golang/x/relay/types/types.go | 26 ++- golang/x/relay/types/types_test.go | 47 ++++ 19 files changed, 1013 insertions(+), 325 deletions(-) create mode 100644 golang/.gitignore rename golang/{app.go => fake_app.go} (50%) create mode 100644 golang/x/relay/types/types_test.go diff --git a/golang/.gitignore b/golang/.gitignore new file mode 100644 index 00000000..9ed3b07c --- /dev/null +++ b/golang/.gitignore @@ -0,0 +1 @@ +*.test diff --git a/golang/cmd/relayd/main.go b/golang/cmd/relayd/main.go index 4dc2011f..fa5aad6a 100644 --- a/golang/cmd/relayd/main.go +++ b/golang/cmd/relayd/main.go @@ -53,7 +53,7 @@ func main() { server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) // prepare and add flags - executor := cli.PrepareBaseCmd(rootCmd, "NS", app.DefaultNodeHome) + executor := cli.PrepareBaseCmd(rootCmd, "RE", app.DefaultNodeHome) err := executor.Execute() if err != nil { panic(err) diff --git a/golang/app.go b/golang/fake_app.go similarity index 50% rename from golang/app.go rename to golang/fake_app.go index 74744faf..81cf7954 100644 --- a/golang/app.go +++ b/golang/fake_app.go @@ -1,4 +1,4 @@ -package relay +package app import ( "encoding/json" @@ -7,12 +7,20 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/genaccounts" + "github.com/cosmos/cosmos-sdk/x/genutil" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" "github.com/tendermint/tendermint/libs/log" "github.com/summa-tx/relays/golang/x/relay" bam "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" tmtypes "github.com/tendermint/tendermint/types" @@ -30,8 +38,26 @@ var ( // ModuleBasics is basic module elemnets ModuleBasics = module.NewBasicManager( + genaccounts.AppModuleBasic{}, + genutil.AppModuleBasic{}, + auth.AppModuleBasic{}, + bank.AppModuleBasic{}, + staking.AppModuleBasic{}, + distr.AppModuleBasic{}, + params.AppModuleBasic{}, + slashing.AppModuleBasic{}, + supply.AppModuleBasic{}, + relay.AppModule{}, ) + + // account permissions + maccPerms = map[string][]string{ + auth.FeeCollectorName: nil, + distr.ModuleName: nil, + staking.BondedPoolName: {supply.Burner, supply.Staking}, + staking.NotBondedPoolName: {supply.Burner, supply.Staking}, + } ) type relayApp struct { @@ -42,6 +68,15 @@ type relayApp struct { keys map[string]*sdk.KVStoreKey tkeys map[string]*sdk.TransientStoreKey + // Keepers + accountKeeper auth.AccountKeeper + bankKeeper bank.Keeper + stakingKeeper staking.Keeper + slashingKeeper slashing.Keeper + distrKeeper distr.Keeper + supplyKeeper supply.Keeper + paramsKeeper params.Keeper + relayKeeper relay.Keeper // Module Manager @@ -65,7 +100,16 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA // BaseApp handles interactions with Tendermint through the ABCI protocol bApp := bam.NewBaseApp(appName, logger, db, auth.DefaultTxDecoder(cdc), baseAppOptions...) - keys := sdk.NewKVStoreKeys(relay.StoreKey) + keys := sdk.NewKVStoreKeys( + bam.MainStoreKey, + auth.StoreKey, + staking.StoreKey, + supply.StoreKey, + distr.StoreKey, + slashing.StoreKey, + params.StoreKey, + relay.StoreKey) + tkeys := sdk.NewTransientStoreKeys(staking.TStoreKey, params.TStoreKey) // Here you initialize your application with the store keys it requires var app = &relayApp{ @@ -75,6 +119,77 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA tkeys: tkeys, } + // The ParamsKeeper handles parameter storage for the application + app.paramsKeeper = params.NewKeeper(app.cdc, keys[params.StoreKey], tkeys[params.TStoreKey], params.DefaultCodespace) + // Set specific supspaces + authSubspace := app.paramsKeeper.Subspace(auth.DefaultParamspace) + bankSubspace := app.paramsKeeper.Subspace(bank.DefaultParamspace) + stakingSubspace := app.paramsKeeper.Subspace(staking.DefaultParamspace) + distrSubspace := app.paramsKeeper.Subspace(distr.DefaultParamspace) + slashingSubspace := app.paramsKeeper.Subspace(slashing.DefaultParamspace) + + // The AccountKeeper handles address -> account lookups + app.accountKeeper = auth.NewAccountKeeper( + app.cdc, + keys[auth.StoreKey], + authSubspace, + auth.ProtoBaseAccount, + ) + + // The BankKeeper allows you perform sdk.Coins interactions + app.bankKeeper = bank.NewBaseKeeper( + app.accountKeeper, + bankSubspace, + bank.DefaultCodespace, + app.ModuleAccountAddrs(), + ) + + // The SupplyKeeper collects transaction fees and renders them to the fee distribution module + app.supplyKeeper = supply.NewKeeper( + app.cdc, + keys[supply.StoreKey], + app.accountKeeper, + app.bankKeeper, + maccPerms, + ) + + // The staking keeper + stakingKeeper := staking.NewKeeper( + app.cdc, + keys[staking.StoreKey], + tkeys[staking.TStoreKey], + app.supplyKeeper, + stakingSubspace, + staking.DefaultCodespace, + ) + + app.distrKeeper = distr.NewKeeper( + app.cdc, + keys[distr.StoreKey], + distrSubspace, + &stakingKeeper, + app.supplyKeeper, + distr.DefaultCodespace, + auth.FeeCollectorName, + app.ModuleAccountAddrs(), + ) + + app.slashingKeeper = slashing.NewKeeper( + app.cdc, + keys[slashing.StoreKey], + &stakingKeeper, + slashingSubspace, + slashing.DefaultCodespace, + ) + + // register the staking hooks + // NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks + app.stakingKeeper = *stakingKeeper.SetHooks( + staking.NewMultiStakingHooks( + app.distrKeeper.Hooks(), + app.slashingKeeper.Hooks()), + ) + // The RelayKeeper is the Keeper from the module for this tutorial // It handles interactions with the store app.relayKeeper = relay.NewKeeper( @@ -84,15 +199,30 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA ) app.mm = module.NewManager( + genaccounts.NewAppModule(app.accountKeeper), + genutil.NewAppModule(app.accountKeeper, app.stakingKeeper, app.BaseApp.DeliverTx), + auth.NewAppModule(app.accountKeeper), + bank.NewAppModule(app.bankKeeper, app.accountKeeper), relay.NewAppModule(app.relayKeeper), + supply.NewAppModule(app.supplyKeeper, app.accountKeeper), + distr.NewAppModule(app.distrKeeper, app.supplyKeeper), + slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper), + staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper), ) - // app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) - // app.mm.SetOrderEndBlockers(staking.ModuleName) + app.mm.SetOrderBeginBlockers(distr.ModuleName, slashing.ModuleName) + app.mm.SetOrderEndBlockers(staking.ModuleName) // Sets the order of Genesis - Order matters, genutil is to always come last app.mm.SetOrderInitGenesis( + genaccounts.ModuleName, + distr.ModuleName, + staking.ModuleName, + auth.ModuleName, + bank.ModuleName, + slashing.ModuleName, relay.ModuleName, + genutil.ModuleName, ) // register all module routes and module queriers @@ -103,8 +233,18 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA app.SetBeginBlocker(app.BeginBlocker) app.SetEndBlocker(app.EndBlocker) + // The AnteHandler handles signature verification and transaction pre-processing + app.SetAnteHandler( + auth.NewAnteHandler( + app.accountKeeper, + app.supplyKeeper, + auth.DefaultSigVerificationGasConsumer, + ), + ) + // initialize stores app.MountKVStores(keys) + app.MountTransientStores(tkeys) err := app.LoadLatestVersion(app.keys[bam.MainStoreKey]) if err != nil { @@ -148,6 +288,10 @@ func (app *relayApp) LoadHeight(height int64) error { // ModuleAccountAddrs returns all the app's module account addresses. func (app *relayApp) ModuleAccountAddrs() map[string]bool { modAccAddrs := make(map[string]bool) + for acc := range maccPerms { + modAccAddrs[supply.NewModuleAddress(acc).String()] = true + } + return modAccAddrs } @@ -165,5 +309,7 @@ func (app *relayApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteLi return nil, nil, err } + validators = staking.WriteValidators(ctx, app.stakingKeeper) + return appState, validators, nil } diff --git a/golang/go.mod b/golang/go.mod index a4e44b24..59651c0f 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -6,7 +6,7 @@ require ( github.com/bombsimon/wsl v1.2.8 // indirect github.com/cosmos/cosmos-sdk v0.37.0 github.com/go-critic/go-critic v0.4.0 // indirect - github.com/gogo/protobuf v1.3.1 // indirect + github.com/gogo/protobuf v1.3.1 github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d // indirect github.com/golangci/golangci-lint v1.21.0 // indirect github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 // indirect diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index e1858950..070c699f 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "strconv" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -10,16 +11,8 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) -type QueryResGetParent struct { - Digest types.Hash256Digest `json:"digest"` -} - -func (d *QueryResGetParent) String() string { - return fmt.Sprintf("%d", d.Digest) -} - // GetQueryCmd sets up query CLI commands -func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { +func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { relayQueryCommand := &cobra.Command{ Use: types.ModuleName, Short: "Querying commands for the relay module", @@ -28,28 +21,298 @@ func GetQueryCmd(storeKey string, cdc *codec.Codec) *cobra.Command { RunE: client.ValidateCmd, } relayQueryCommand.AddCommand(client.GetCommands( - GetCommandGetParent(storeKey, cdc), + GetCmdIsAncestor(queryRoute, cdc), + GetCmdGetRelayGenesis(queryRoute, cdc), + GetCmdGetLastReorgLCA(queryRoute, cdc), + GetCmdFindAncestor(queryRoute, cdc), + GetCmdIsMostRecentCommonAncestor(queryRoute, cdc), + GetCmdHeaviestFromAncestor(queryRoute, cdc), )...) return relayQueryCommand } -// GetCommandGetParent queries information about a name -func GetCommandGetParent(queryRoute string, cdc *codec.Codec) *cobra.Command { +// GetCmdIsAncestor returns the CLI command struct for IsAncestor +func GetCmdIsAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + // what are the arguments. <> for required, [] for optional + Use: "isancestor [limit]", + Example: "isancestor 12..ab 34..cd 200", // how do you use it? + // a help message. shows on `help isancestor` + Long: "Check if the second argument is an ancestor of the the argument. Optionally set a limit on block traversal", + // how many arguments does it take? + // also useful: cobra.ExactArgs(3) + Args: cobra.RangeArgs(2, 3), + // what does it do when run? + RunE: func(cmd *cobra.Command, args []string) error { + // spin up a context + cliCtx := context.NewCLIContext().WithCodec(cdc) + + var limit uint32 + if len(args) == 3 { + lim, err := strconv.ParseUint(args[2], 0, 32) + if err != nil { + fmt.Print(err.Error()) + return nil + } + limit = uint32(lim) + } + + digestLE, sdkErr := types.Hash256DigestFromHex(args[0]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + ancestor, sdkErr := types.Hash256DigestFromHex(args[1]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + params := types.QueryParamsIsAncestor{ + DigestLE: digestLE, + ProspectiveAncestor: ancestor, + Limit: limit, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + // run the query. the routeString is passed as strings to our querier switch/case in `keeper/querier.go` + res, _, err := cliCtx.QueryWithData("custom/relay/isancestor", queryData) + + if err != nil { + fmt.Printf("could not check if %s... is ancestor of %s... \n", args[1][:8], args[0][:8]) + return nil + } + + var out types.QueryResIsAncestor + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + +// GetCmdGetRelayGenesis returns the CLI command struct for GetRelayGenesis +func GetCmdGetRelayGenesis(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "getrelaygenesis", + Example: "getrelaygenesis", + Long: "Get the first digest in the relay", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData("custom/relay/getrelaygenesis", nil) + + if err != nil { + fmt.Println("could not get the first digest in the relay") + return nil + } + + var out types.QueryResGetRelayGenesis + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + +// GetCmdIsAncestor returns the CLI command struct for IsAncestor +func GetCmdGetLastReorgLCA(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "getlastreorglca", + Example: "getlastreorglca", + Long: "Returns the best known digest in the relay", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData("custom/relay/getlastreorglca", nil) + + if err != nil { + fmt.Println("could not get the last Reorg LCA") + return nil + } + + var out types.QueryResGetLastReorgLCA + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + +func GetCmdFindAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "findancestor ", + Example: "findancestor 12..ab 2", + Long: "Finds the digest blocks before . Errors if digest or the ancestor is unknown", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + digest, sdkErr := types.Hash256DigestFromHex(args[0]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + var offset uint32 + off, err := strconv.ParseUint(args[1], 0, 32) + if err != nil { + fmt.Print(err.Error()) + return nil + } + offset = uint32(off) + + params := types.QueryParamsFindAncestor{ + DigestLE: digest, + Offset: offset, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/findancestor", queryData) + + if err != nil { + fmt.Printf("could not find ancestor of %s... \n", args[0][:8]) + return nil + } + + var out types.QueryResFindAncestor + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + +// GetCmdIsAncestor returns the CLI command struct for IsAncestor +func GetCmdIsMostRecentCommonAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "ismostrecentcommonancestor [limit]", + Example: "ismostrecentcommonancestor 12..ab 34..cd 56..ef 200", // how do you use it? + Long: "Checks if is the LCA of and digests", + Args: cobra.RangeArgs(3, 4), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + ancestor, sdkErr := types.Hash256DigestFromHex(args[0]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + left, sdkErr := types.Hash256DigestFromHex(args[1]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + right, sdkErr := types.Hash256DigestFromHex(args[1]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + var limit uint32 + if len(args) == 4 { + lim, err := strconv.ParseUint(args[3], 0, 32) + if err != nil { + fmt.Print(err.Error()) + return nil + } + limit = uint32(lim) + } + + params := types.QueryParamsIsMostRecentCommonAncestor{ + Ancestor: ancestor, + Left: left, + Right: right, + Limit: limit, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/ismostrecentcommonancestor", queryData) + + if err != nil { + fmt.Printf("could not check if %s... is the LCA of %s... and %s... \n", args[0][:8], args[1][:8], args[2][:8]) + return nil + } + + var out types.QueryResIsMostRecentCommonAncestor + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + +// GetCmdHeaviestFromAncestor returns the CLI command struct for HeaviestFromAncestor +func GetCmdHeaviestFromAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "getparent [digest]", - Short: "getparent digest", - Args: cobra.ExactArgs(1), + Use: "heaviestfromancestor [limit]", + Example: "heaviestFromancestor 12..ab 34..cd 56..ef 200", // how do you use it? + Long: "Determines the heavier descendant of a common ancestor", + Args: cobra.RangeArgs(3, 4), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - digest := args[0] - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/getparent/%s", queryRoute, digest), nil) + ancestor, sdkErr := types.Hash256DigestFromHex(args[0]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + currentBest, sdkErr := types.Hash256DigestFromHex(args[1]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + newBest, sdkErr := types.Hash256DigestFromHex(args[2]) + if sdkErr != nil { + fmt.Print(sdkErr.Error()) + return nil + } + + var limit uint32 + if len(args) == 4 { + lim, err := strconv.ParseUint(args[3], 0, 32) + if err != nil { + fmt.Print(err.Error()) + return nil + } + limit = uint32(lim) + } + + params := types.QueryParamsHeaviestFromAncestor{ + Ancestor: ancestor, + CurrentBest: currentBest, + NewBest: newBest, + Limit: limit, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/heaviestfromancestor", queryData) + if err != nil { - fmt.Printf("could not get parent - %s \n", digest) + fmt.Printf("could not determine if %s... or %s... is heaviest decendant of %s... \n", args[1][:8], args[2][:8], args[0][:8]) return nil } - var out QueryResGetParent + var out types.QueryResHeaviestFromAncestor cdc.MustUnmarshalJSON(res, &out) return cliCtx.PrintOutput(&out) }, diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index 1185f908..ae9ec14a 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -1,22 +1,253 @@ package rest import ( - "fmt" + "encoding/json" "net/http" + "strconv" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/gorilla/mux" + + "github.com/summa-tx/relays/golang/x/relay/types" ) -func getParentHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { +// handler function for isAncestor queries. parses arguments from url string, and passes them through +// as a QueryParamsIsAncestor struct +func isAncestorHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // mux.Vars holds the variable elements of the URL from rest.go + vars := mux.Vars(r) + + digestLE, sdkErr := types.Hash256DigestFromHex(vars["digest"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + ancestor, sdkErr := types.Hash256DigestFromHex(vars["ancestor"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + var limit uint32 + if val, ok := vars["limit"]; ok { + lim, err := strconv.ParseUint(val, 0, 32) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + limit = uint32(lim) + } + + params := types.QueryParamsIsAncestor{ + DigestLE: digestLE, + ProspectiveAncestor: ancestor, + Limit: limit, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // run the query. the routeString is passed as strings to our querier switch/case in `keeper/querier.go` + res, _, err := cliCtx.QueryWithData("custom/relay/isancestor", queryData) + + // below this is boilerplate + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// handler function for getRelayGenesis queries +func getRelayGenesisHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + res, _, err := cliCtx.QueryWithData("custom/relay/getrelaygenesis", nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// handler function for getLastReorgLCA queries +func getLastReorgLCAHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + res, _, err := cliCtx.QueryWithData("custom/relay/getlastreorglca", nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// handler function for findAncestor queries. parses arguments from url string, and passes them through +// as a QueryParamsFindAncestor struct +func findAncestorHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // mux.Vars holds the variable elements of the URL from rest.go vars := mux.Vars(r) - paramType := vars[restName] - res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/resolve/%s", storeName, paramType), nil) + digestLE, sdkErr := types.Hash256DigestFromHex(vars["digest"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + off, err := strconv.ParseUint(vars["offset"], 0, 32) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + offset := uint32(off) + + params := types.QueryParamsFindAncestor{ + DigestLE: digestLE, + Offset: offset, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // run the query. the routeString is passed as strings to our querier switch/case in `keeper/querier.go` + res, _, err := cliCtx.QueryWithData("custom/relay/findancestor", queryData) + + // below this is boilerplate + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// handler function for IsMostRecentCommonAncestor queries. parses arguments from url string, and passes them through +// as a QueryParamsIsMostRecentCommonAncestor struct +func isMostRecentCommonAncestorHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // mux.Vars holds the variable elements of the URL from rest.go + vars := mux.Vars(r) + + ancestor, sdkErr := types.Hash256DigestFromHex(vars["ancestor"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + left, sdkErr := types.Hash256DigestFromHex(vars["left"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + right, sdkErr := types.Hash256DigestFromHex(vars["right"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + var limit uint32 + if val, ok := vars["limit"]; ok { + lim, err := strconv.ParseUint(val, 0, 32) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + limit = uint32(lim) + } + + params := types.QueryParamsIsMostRecentCommonAncestor{ + Ancestor: ancestor, + Left: left, + Right: right, + Limit: limit, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // run the query. the routeString is passed as strings to our querier switch/case in `keeper/querier.go` + res, _, err := cliCtx.QueryWithData("custom/relay/ismostrecentcommonancestor", queryData) + + // below this is boilerplate + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +// handler function for heaviestFromAncestor queries. parses arguments from url string, and passes them +// through as a QueryParamsHeaviestFromAncestor struct +func heaviestFromAncestorHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // mux.Vars holds the variable elements of the URL from rest.go + vars := mux.Vars(r) + + ancestor, sdkErr := types.Hash256DigestFromHex(vars["ancestor"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + currentBest, sdkErr := types.Hash256DigestFromHex(vars["currentBest"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + newBest, sdkErr := types.Hash256DigestFromHex(vars["newBest"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + + var limit uint32 + if val, ok := vars["limit"]; ok { + lim, err := strconv.ParseUint(val, 0, 32) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) + return + } + limit = uint32(lim) + } + + params := types.QueryParamsHeaviestFromAncestor{ + Ancestor: ancestor, + CurrentBest: currentBest, + NewBest: newBest, + Limit: limit, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + // run the query. the routeString is passed as strings to our querier switch/case in `keeper/querier.go` + res, _, err := cliCtx.QueryWithData("custom/relay/heaviestfromancestor", queryData) + + // below this is boilerplate if err != nil { rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index a6f42314..570ec33a 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -8,12 +8,22 @@ import ( "github.com/gorilla/mux" ) -const ( - restName = "relay" -) - // RegisterRoutes - Central function to define routes that get registered by the main application func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { - r.HandleFunc(fmt.Sprintf("/%s/link", storeName), setLinkHandler(cliCtx)).Methods("POST") - r.HandleFunc(fmt.Sprintf("/%s/parent/{%s}", storeName, restName), getParentHandler(cliCtx, storeName)).Methods("GET") + s := r.PathPrefix(fmt.Sprintf("/%s", storeName)).Subrouter() + + // r.HandleFunc(fmt.Sprintf("/%s/link", storeName), setLinkHandler(cliCtx)).Methods("POST") + + // add new query routes below + // {} denotes variable parts of the url route + // These are our function arguments + s.HandleFunc("/isancestor/{digest}/{ancestor}/", isAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/isancestor/{digest}/{ancestor}/{limit}", isAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/getrelaygenesis", getRelayGenesisHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/getlastreorglca", getLastReorgLCAHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/findancestor/{digest}/{offset}", findAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/ismostrecentcommonancestor/{ancestor}/{left}/{right}/", isMostRecentCommonAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/ismostrecentcommonancestor/{ancestor}/{left}/{right}/{limit}", isMostRecentCommonAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/{limit}", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") } diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index b2be731f..30baa05e 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -1,48 +1,37 @@ package rest -import ( - "net/http" - - "github.com/summa-tx/relays/golang/x/relay/types" - - "github.com/cosmos/cosmos-sdk/client/context" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/rest" - "github.com/cosmos/cosmos-sdk/x/auth/client/utils" -) - -type setLinkReq struct { - BaseReq rest.BaseReq `json:"base_req"` - Header string `json:"header"` - Sender sdk.AccAddress `json:"sender"` -} - -func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - var req setLinkReq - - if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { - rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") - return - } - - baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { - return - } - - addr := sdk.AccAddress(req.Sender) - - // create the message - msg := types.NewMsgSetLink(req.Sender, req.Header, addr) - err := msg.ValidateBasic() - - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) - } -} +// +// type setLinkReq struct { +// BaseReq rest.BaseReq `json:"base_req"` +// Header string `json:"header"` +// Sender sdk.AccAddress `json:"sender"` +// } +// +// func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { +// return func(w http.ResponseWriter, r *http.Request) { +// var req setLinkReq +// +// if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { +// rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") +// return +// } +// +// baseReq := req.BaseReq.Sanitize() +// if !baseReq.ValidateBasic(w) { +// return +// } +// +// addr := sdk.AccAddress(req.Sender) +// +// // create the message +// msg := types.NewMsgSetLink(req.Sender, req.Header, addr) +// err := msg.ValidateBasic() +// +// if err != nil { +// rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) +// return +// } +// +// utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) +// } +// } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index c55c2389..3481bd1f 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -32,11 +32,6 @@ func (k Keeper) setDigestByStoreKey(ctx sdk.Context, key string, digest types.Ha store.Set([]byte(key), digest[:]) } -// GetRelayGenesis returns the first digest in the relay -func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { - return k.getDigestByStoreKey(ctx, types.RelayGenesisStorage) -} - // setBestKnownDigest sets the best known chain tip func (k Keeper) setBestKnownDigest(ctx sdk.Context, bestKnown types.Hash256Digest) { k.setDigestByStoreKey(ctx, types.BestKnownDigestStorage, bestKnown) diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 9dd725fe..9561410e 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -1,40 +1,40 @@ package keeper -func (suite *KeeperSuite) TestValidateHeaderChain() { - cases := suite.Fixtures.HeaderTestCases.ValidateChain +func (s *KeeperSuite) TestValidateHeaderChain() { + cases := s.Fixtures.HeaderTestCases.ValidateChain for _, tc := range cases { err := validateHeaderChain(tc.Anchor, tc.Headers, tc.Internal, tc.IsMainnet) if tc.Output == 0 { logIfTestCaseError(tc, err) - suite.Nil(err) + s.Nil(err) } else { - suite.NotNil(err) - suite.Equal(tc.Output, err.Code()) + s.NotNil(err) + s.Equal(tc.Output, err.Code()) } } } -func (suite *KeeperSuite) TestValidateDifficultyChange() { - cases := suite.Fixtures.HeaderTestCases.ValidateDiffChange +func (s *KeeperSuite) TestValidateDifficultyChange() { + cases := s.Fixtures.HeaderTestCases.ValidateDiffChange for _, tc := range cases { err := validateDifficultyChange(tc.Headers, tc.PrevEpochStart, tc.Anchor) if tc.Output == 0 { logIfTestCaseError(tc, err) - suite.Nil(err) + s.Nil(err) } else { - suite.NotNil(err) - suite.Equal(tc.Output, err.Code()) + s.NotNil(err) + s.Equal(tc.Output, err.Code()) } } } -func (suite *KeeperSuite) TestCompareTargets() { - cases := suite.Fixtures.HeaderTestCases.CompareTargets +func (s *KeeperSuite) TestCompareTargets() { + cases := s.Fixtures.HeaderTestCases.CompareTargets for _, tc := range cases { result := compareTargets(tc.Full, tc.Truncated) - suite.Equal(result, tc.Output) + s.Equal(result, tc.Output) } } diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 658a59b0..0f58f93f 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -39,6 +39,11 @@ func (k Keeper) setRelayGenesis(ctx sdk.Context, relayGenesis types.Hash256Diges k.setDigestByStoreKey(ctx, types.RelayGenesisStorage, relayGenesis) } +// GetRelayGenesis returns the first digest in the relay +func (k Keeper) GetRelayGenesis(ctx sdk.Context) (types.Hash256Digest, sdk.Error) { + return k.getDigestByStoreKey(ctx, types.RelayGenesisStorage) +} + // SetGenesisState sets the genesis state func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.BitcoinHeader) sdk.Error { if k.hasRelayGenesis(ctx) { diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 207d2d1b..e2332fee 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -7,7 +7,17 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/supply" + abci "github.com/tendermint/tendermint/abci/types" + tmlog "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" + "github.com/stretchr/testify/suite" "github.com/summa-tx/relays/golang/x/relay/types" @@ -65,6 +75,8 @@ type KeeperTestCases struct { type KeeperSuite struct { suite.Suite Fixtures KeeperTestCases + Context sdk.Context + Keeper Keeper } func (c Case) Name() string { @@ -83,6 +95,41 @@ func logIfTestCaseError(tc NamedCase, err sdk.Error) { } } +func (s *KeeperSuite) InitTestContext(mainnet, isCheckTx bool) { + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keySupply := sdk.NewKVStoreKey(supply.StoreKey) + + relayKey := sdk.NewKVStoreKey(types.StoreKey) + + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(relayKey, sdk.StoreTypeIAVL, db) + err := ms.LoadLatestVersion() + if err != nil { + panic(err.Error()) + } + + cdc := codec.New() + + ctx := sdk.NewContext(ms, abci.Header{ChainID: "relayTestChain"}, isCheckTx, tmlog.NewNopLogger()) + keeper := NewKeeper(relayKey, cdc, mainnet) + + s.Context = ctx + s.Keeper = keeper +} + +func (s *KeeperSuite) SetupTest() { + s.InitTestContext(true, false) +} + // Runs the whole test suite func TestKeeper(t *testing.T) { jsonFile, err := os.Open("../../../../testVectors.json") @@ -101,3 +148,37 @@ func TestKeeper(t *testing.T) { suite.Run(t, keeperSuite) } + +func (s *KeeperSuite) TestGetPrefixStore() { + prefStore := s.Keeper.getPrefixStore(s.Context, "toast-") + store := s.Context.KVStore(s.Keeper.storeKey) + + expected := []byte{0xff} + + prefStore.Set([]byte("1"), expected) + actual := store.Get([]byte("toast-1")) + + s.Equal(expected, actual) +} + +func (s *KeeperSuite) TestSetGenesisState() { + genesis := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].Anchor + epochStart := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].PrevEpochStart + err := s.Keeper.SetGenesisState(s.Context, genesis, epochStart) + s.Nil(err) + + gen, err := s.Keeper.GetRelayGenesis(s.Context) + s.Nil(err) + s.Equal(genesis.HashLE, gen) + + lca, err := s.Keeper.GetLastReorgLCA(s.Context) + s.Nil(err) + s.Equal(genesis.HashLE, lca) + + best, err := s.Keeper.GetBestKnownDigest(s.Context) + s.Nil(err) + s.Equal(genesis.HashLE, best) + + err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) + s.Equal(types.AlreadyInit, err.Code()) +} diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index a036a65e..017a90d0 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -37,7 +37,7 @@ func (k Keeper) FindAncestor(ctx sdk.Context, digestLE types.Hash256Digest, offs } for i := uint32(0); i < offset; i++ { - current := k.getLink(ctx, current) + current = k.getLink(ctx, current) if !k.hasLink(ctx, current) { return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) } @@ -47,14 +47,14 @@ func (k Keeper) FindAncestor(ctx sdk.Context, digestLE types.Hash256Digest, offs } // IsAncestor checks whether -func (k Keeper) IsAncestor(ctx sdk.Context, ancestor, digestLE types.Hash256Digest, limit uint32) bool { +func (k Keeper) IsAncestor(ctx sdk.Context, digestLE, ancestor types.Hash256Digest, limit uint32) bool { current := digestLE for i := uint32(0); i < limit; i++ { if !k.hasLink(ctx, current) { return false } - current := k.getLink(ctx, current) + current = k.getLink(ctx, current) if current == ancestor { return true } diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index 0eb1d88e..f0c1fd5a 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -1,47 +1,15 @@ package keeper import ( - "encoding/hex" + "fmt" "strconv" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" abci "github.com/tendermint/tendermint/abci/types" ) -// QueryIsAncestor is a query string tag for IsAncestor -const QueryIsAncestor = "isancestor" - -// QueryGetRelayGenesis is a query string tag for GetRelayGenesis -const QueryGetRelayGenesis = "getrelaygenesis" - -// QueryGetLastReorgLCA is a query string tag for GetLastReorgLCA -const QueryGetLastReorgLCA = "getlastreorglca" - -// QueryFindAncestor is a query string tag for FindAncestor -const QueryFindAncestor = "findancestor" - -// QueryHeaviestFromAncestor is a query string tag for HeaviestFromAncestor -const QueryHeaviestFromAncestor = "heaviestfromancestor" - -// QueryIsMostRecentCommonAncestor is a query string tag for IsMostRecentCommonAncestor -const QueryIsMostRecentCommonAncestor = "ismostrecentcommonancestor" - -// hash256DigestFromHex converts a hex into a Hash256Digest -func hash256DigestFromHex(hexStr string) (types.Hash256Digest, sdk.Error) { - bytes, decodeErr := hex.DecodeString(hexStr) - if decodeErr != nil { - return types.Hash256Digest{}, types.ErrBadHex(types.DefaultCodespace) - } - digest, newDigestErr := btcspv.NewHash256Digest(bytes) - if newDigestErr != nil { - return types.Hash256Digest{}, types.FromBTCSPVError(types.DefaultCodespace, newDigestErr) - } - return digest, nil -} - func decodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, sdk.Error) { if idx+1 > len(path) { return defaultLimit, nil @@ -58,66 +26,44 @@ func decodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - case QueryIsAncestor: - return queryIsAncestor(ctx, path[1:], req, keeper) - case QueryGetRelayGenesis: + case types.QueryIsAncestor: + return queryIsAncestor(ctx, req, keeper) + case types.QueryGetRelayGenesis: return queryGetRelayGenesis(ctx, req, keeper) - case QueryGetLastReorgLCA: + case types.QueryGetLastReorgLCA: return queryGetLastReorgLCA(ctx, req, keeper) - case QueryFindAncestor: - return queryFindAncestor(ctx, path[1:], req, keeper) - case QueryHeaviestFromAncestor: - return queryHeaviestFromAncestor(ctx, path[1:], req, keeper) - case QueryIsMostRecentCommonAncestor: - return queryIsMostRecentCommonAncestor(ctx, path[1:], req, keeper) + case types.QueryFindAncestor: + return queryFindAncestor(ctx, req, keeper) + case types.QueryHeaviestFromAncestor: + return queryHeaviestFromAncestor(ctx, req, keeper) + case types.QueryIsMostRecentCommonAncestor: + return queryIsMostRecentCommonAncestor(ctx, req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } } } -func queryIsAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - // Take some URL path, parse the arguments out of it, pass it to the keeper, - // and format the result as a QueryResIsAncestor - - // The path I expect here looks like this: - // /getancestor/abcd1234.../second_digest/limit - - // getAncestor is removed by the handler switch/case block above - - // check that the path is this many items long, error if not - if len(path) > 3 { - return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) - } else if len(path) < 2 { - return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) - } - - // parse the first path item as hex. - digestLE, digestErr := hash256DigestFromHex(path[0]) - if digestErr != nil { - return []byte{}, digestErr - } +func queryIsAncestor(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsIsAncestor - // parse the second path item as hex. - ancestor, ancestorErr := hash256DigestFromHex(path[1]) - if ancestorErr != nil { - return []byte{}, ancestorErr + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) } - limit, limitErr := decodeUint32FromPath(path, 2, 15) - if limitErr != nil { - return []byte{}, limitErr + limit := params.Limit + if limit == 0 { + limit = types.DefaultLookupLimit } // This calls the keeper with the parsed arguments, and gets an answer - result := keeper.IsAncestor(ctx, ancestor, digestLE, uint32(limit)) + result := keeper.IsAncestor(ctx, params.DigestLE, params.ProspectiveAncestor, limit) // Now we format the answer as a response response := types.QueryResIsAncestor{ - Digest: digestLE, - ProspectiveAncestor: ancestor, - Limit: limit, - Res: result, + Params: params, + Res: result, } // And we serialize that response as JSON @@ -168,36 +114,24 @@ func queryGetLastReorgLCA(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) return res, nil } -func queryFindAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - // check that the path is this many items long, error if not - if len(path) > 2 { - return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) - } else if len(path) < 2 { - return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) - } - - digestLE, digestErr := hash256DigestFromHex(path[0]) - if digestErr != nil { - return []byte{}, digestErr - } +func queryFindAncestor(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsFindAncestor - offset, convErr := strconv.ParseUint(path[1], 0, 32) - if convErr != nil { - return []byte{}, types.ErrExternal(types.DefaultCodespace, convErr) + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) } - newOffset := uint32(offset) // This calls the keeper with the parsed arguments, and gets an answer - result, err := keeper.FindAncestor(ctx, digestLE, newOffset) + result, err := keeper.FindAncestor(ctx, params.DigestLE, params.Offset) if err != nil { return []byte{}, err } // Now we format the answer as a response response := types.QueryResFindAncestor{ - DigestLE: digestLE, - Offset: newOffset, - Res: result, + Params: params, + Res: result, } // And we serialize that response as JSON @@ -208,48 +142,29 @@ func queryFindAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, ke return res, nil } -func queryHeaviestFromAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - // check that the path is this many items long, error if not - if len(path) > 4 { - return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) - } else if len(path) < 3 { - return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) - } - - ancestor, ancestorErr := hash256DigestFromHex(path[0]) - if ancestorErr != nil { - return []byte{}, ancestorErr - } +func queryHeaviestFromAncestor(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsHeaviestFromAncestor - // parse the second path item as hex. - currentBestDigest, currentBestErr := hash256DigestFromHex(path[1]) - if currentBestErr != nil { - return []byte{}, currentBestErr + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) } - newBestDigest, newBestErr := hash256DigestFromHex(path[2]) - if newBestErr != nil { - return []byte{}, newBestErr - } - - limit, limitErr := decodeUint32FromPath(path, 3, 15) - if limitErr != nil { - return []byte{}, limitErr + limit := params.Limit + if limit == 0 { + limit = types.DefaultLookupLimit } // This calls the keeper with the parsed arguments, and gets an answer - result, err := keeper.HeaviestFromAncestor(ctx, ancestor, currentBestDigest, newBestDigest, limit) + result, err := keeper.HeaviestFromAncestor(ctx, params.Ancestor, params.CurrentBest, params.NewBest, limit) if err != nil { return []byte{}, err } // Now we format the answer as a response response := types.QueryResHeaviestFromAncestor{ - Ancestor: ancestor, - CurrentBest: currentBestDigest, - NewBest: newBestDigest, - Limit: limit, - Res: result, + Params: params, + Res: result, } // And we serialize that response as JSON @@ -260,44 +175,26 @@ func queryHeaviestFromAncestor(ctx sdk.Context, path []string, req abci.RequestQ return res, nil } -func queryIsMostRecentCommonAncestor(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - // check that the path is this many items long, error if not - if len(path) > 4 { - return []byte{}, types.ErrTooManyArguments(types.DefaultCodespace) - } else if len(path) < 3 { - return []byte{}, types.ErrNotEnoughArguments(types.DefaultCodespace) - } - - ancestor, ancestorErr := hash256DigestFromHex(path[0]) - if ancestorErr != nil { - return []byte{}, ancestorErr - } - - leftDigest, leftErr := hash256DigestFromHex(path[1]) - if leftErr != nil { - return []byte{}, leftErr - } +func queryIsMostRecentCommonAncestor(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsIsMostRecentCommonAncestor - rightDigest, rightErr := hash256DigestFromHex(path[2]) - if rightErr != nil { - return []byte{}, rightErr + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) } - limit, limitErr := decodeUint32FromPath(path, 3, 15) - if limitErr != nil { - return []byte{}, limitErr + limit := params.Limit + if limit == 0 { + limit = types.DefaultLookupLimit } // This calls the keeper with the parsed arguments, and gets an answer - result := keeper.IsMostRecentCommonAncestor(ctx, ancestor, leftDigest, rightDigest, limit) + result := keeper.IsMostRecentCommonAncestor(ctx, params.Ancestor, params.Left, params.Right, limit) // Now we format the answer as a response response := types.QueryResIsMostRecentCommonAncestor{ - Ancestor: ancestor, - Left: leftDigest, - Right: rightDigest, - Limit: limit, - Res: result, + Params: params, + Res: result, } // And we serialize that response as JSON diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index d2c53eed..9a779e11 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -5,41 +5,6 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) -func (suite *KeeperSuite) TestHash256DigestFromHex() { - Hash256FromHexPass := []struct { - Input string - Output types.Hash256Digest - }{ - { - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - types.Hash256Digest{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, - }, - } - - Hash256FromHexFail := []struct { - Input string - Err sdk.CodeType - }{ - { - "jjjjjj", - types.BadHex, - }, { - "ffffff", - types.BitcoinSPV, - }, - } - - for i := range Hash256FromHexPass { - digest, err := hash256DigestFromHex(Hash256FromHexPass[i].Input) - suite.Nil(err) - suite.Equal(digest, Hash256FromHexPass[i].Output) - } - for i := range Hash256FromHexFail { - _, err := hash256DigestFromHex(Hash256FromHexFail[i].Input) - suite.Equal(Hash256FromHexFail[i].Err, err.Code()) - } -} - func (suite *KeeperSuite) TestDecodeUint32FromPath() { DecodeUintPass := []struct { Path []string diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 076529e5..fcea3a98 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -107,6 +107,7 @@ const ( // 500-block Queries + // TODO: Delete the next 2 error codes // NotEnoughArguments means there are not enough arguments specified in the path of a query NotEnoughArguments sdk.CodeType = 501 // NotEnoughArgumentsMessage is the corresponding message diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 0bb4a781..a56dbbe0 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -5,21 +5,49 @@ import ( "fmt" ) -// QueryResIsAncestor is the response struct for queryIsAncestor -type QueryResIsAncestor struct { - Digest Hash256Digest `json:"digest"` +const ( + // DefaultLookupLimit is the default limit for lookup requests + DefaultLookupLimit = 18 + + // QueryIsAncestor is a query string tag for IsAncestor + QueryIsAncestor = "isancestor" + + // QueryGetRelayGenesis is a query string tag for GetRelayGenesis + QueryGetRelayGenesis = "getrelaygenesis" + + // QueryGetLastReorgLCA is a query string tag for GetLastReorgLCA + QueryGetLastReorgLCA = "getlastreorglca" + + // QueryFindAncestor is a query string tag for FindAncestor + QueryFindAncestor = "findancestor" + + // QueryHeaviestFromAncestor is a query string tag for HeaviestFromAncestor + QueryHeaviestFromAncestor = "heaviestfromancestor" + + // QueryIsMostRecentCommonAncestor is a query string tag for IsMostRecentCommonAncestor + QueryIsMostRecentCommonAncestor = "ismostrecentcommonancestor" +) + +// QueryParamsIsAncestor represents the parameters for an IsAncestor query +type QueryParamsIsAncestor struct { + DigestLE Hash256Digest `json:"digest"` ProspectiveAncestor Hash256Digest `json:"prospectiveAncestor"` Limit uint32 `json:"limit"` - Res bool `json:"result"` +} + +// QueryResIsAncestor is the response to a IsAncestor query +type QueryResIsAncestor struct { + Params QueryParamsIsAncestor `json:"params"` + Res bool `json:"result"` } // String formats a QueryResIsAncestor struct func (r QueryResIsAncestor) String() string { - dig := "0x" + hex.EncodeToString(r.Digest[:]) - digAnc := "0x" + hex.EncodeToString(r.ProspectiveAncestor[:]) + dig := "0x" + hex.EncodeToString(r.Params.DigestLE[:]) + digAnc := "0x" + hex.EncodeToString(r.Params.ProspectiveAncestor[:]) return fmt.Sprintf( "Digest: %s, Ancestor: %s, Limit: %d, Result: %t", - dig, digAnc, r.Limit, r.Res) + dig, digAnc, r.Params.Limit, r.Res) } // QueryResGetRelayGenesis is the response struct for queryGetRelayGenesis @@ -44,58 +72,71 @@ func (r QueryResGetLastReorgLCA) String() string { return fmt.Sprintf("%s\n", digest) } -// QueryResFindAncestor is the response struct for queryFindAncestor -type QueryResFindAncestor struct { +// QueryParamsFindAncestor represents the parameters for a FindAncestor query +type QueryParamsFindAncestor struct { DigestLE Hash256Digest `json:"digestLE"` Offset uint32 `json:"offset"` - Res Hash256Digest `json:"result"` +} + +// QueryResFindAncestor is the response struct for queryFindAncestor +type QueryResFindAncestor struct { + Params QueryParamsFindAncestor `json:"params"` + Res Hash256Digest `json:"result"` } // String formats a QueryResFindAncestor struct func (r QueryResFindAncestor) String() string { - dig := "0x" + hex.EncodeToString(r.DigestLE[:]) - offset := r.Offset + dig := "0x" + hex.EncodeToString(r.Params.DigestLE[:]) + offset := r.Params.Offset res := "0x" + hex.EncodeToString(r.Res[:]) return fmt.Sprintf( "Digest LE: %s, Offset: %d, Result: %s", dig, offset, res) } -// QueryResHeaviestFromAncestor is the response struct for queryHeaviestFromAncestor -type QueryResHeaviestFromAncestor struct { +type QueryParamsHeaviestFromAncestor struct { Ancestor Hash256Digest `json:"ancestor"` CurrentBest Hash256Digest `json:"currentBest"` NewBest Hash256Digest `json:"newBest"` Limit uint32 `json:"limit"` - Res Hash256Digest `json:"result"` +} + +// QueryResHeaviestFromAncestor is the response struct for queryHeaviestFromAncestor +type QueryResHeaviestFromAncestor struct { + Params QueryParamsHeaviestFromAncestor `json:"params"` + Res Hash256Digest `json:"result"` } // String formats a QueryResHeaviestFromAncestor struct func (r QueryResHeaviestFromAncestor) String() string { - anc := "0x" + hex.EncodeToString(r.Ancestor[:]) - curBest := "0x" + hex.EncodeToString(r.CurrentBest[:]) - newBest := "0x" + hex.EncodeToString(r.NewBest[:]) + anc := "0x" + hex.EncodeToString(r.Params.Ancestor[:]) + curBest := "0x" + hex.EncodeToString(r.Params.CurrentBest[:]) + newBest := "0x" + hex.EncodeToString(r.Params.NewBest[:]) res := "0x" + hex.EncodeToString(r.Res[:]) return fmt.Sprintf( "Ancestor: %s, Current Best: %s, New Best: %s, Limit: %d, Result: %s", - anc, curBest, newBest, r.Limit, res) + anc, curBest, newBest, r.Params.Limit, res) } -// QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor -type QueryResIsMostRecentCommonAncestor struct { +type QueryParamsIsMostRecentCommonAncestor struct { Ancestor Hash256Digest `json:"ancestor"` Left Hash256Digest `json:"left"` Right Hash256Digest `json:"right"` Limit uint32 `json:"limit"` - Res bool `json:"result"` +} + +// QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor +type QueryResIsMostRecentCommonAncestor struct { + Params QueryParamsIsMostRecentCommonAncestor `json:"params"` + Res bool `json:"result"` } // String formats a QueryResIsMostRecentCommonAncestor struct func (r QueryResIsMostRecentCommonAncestor) String() string { - anc := "0x" + hex.EncodeToString(r.Ancestor[:]) - left := "0x" + hex.EncodeToString(r.Left[:]) - right := "0x" + hex.EncodeToString(r.Right[:]) + anc := "0x" + hex.EncodeToString(r.Params.Ancestor[:]) + left := "0x" + hex.EncodeToString(r.Params.Left[:]) + right := "0x" + hex.EncodeToString(r.Params.Right[:]) return fmt.Sprintf( "Ancestor: %s, Left: %s, Right: %s, Limit: %d, Result: %t", - anc, left, right, r.Limit, r.Res) + anc, left, right, r.Params.Limit, r.Res) } diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 1a3f5678..584fe0ba 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -1,6 +1,10 @@ package types import ( + "encoding/hex" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) @@ -19,8 +23,20 @@ type BitcoinHeader = btcspv.BitcoinHeader // SPVProof is the base struct for an SPV proof type SPVProof = btcspv.SPVProof -// // Link is a link in the chain -// type Link struct { -// Digest string `json:"digest"` -// Parent string `json:"parent"` -// } +// Hash256DigestFromHex converts a hex into a Hash256Digest +func Hash256DigestFromHex(hexStr string) (Hash256Digest, sdk.Error) { + data := hexStr + if data[:2] == "0x" { + data = data[2:] + } + + bytes, decodeErr := hex.DecodeString(data) + if decodeErr != nil { + return Hash256Digest{}, ErrBadHex(DefaultCodespace) + } + digest, newDigestErr := btcspv.NewHash256Digest(bytes) + if newDigestErr != nil { + return Hash256Digest{}, FromBTCSPVError(DefaultCodespace, newDigestErr) + } + return digest, nil +} diff --git a/golang/x/relay/types/types_test.go b/golang/x/relay/types/types_test.go new file mode 100644 index 00000000..65c9cd3d --- /dev/null +++ b/golang/x/relay/types/types_test.go @@ -0,0 +1,47 @@ +package types + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" +) + +func TestHash256DigestFromHex(t *testing.T) { + Hash256FromHexPass := []struct { + Input string + Output Hash256Digest + }{ + { + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + Hash256Digest{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + { + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + Hash256Digest{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + }, + } + + Hash256FromHexFail := []struct { + Input string + Err sdk.CodeType + }{ + { + "jjjjjj", + BadHex, + }, { + "ffffff", + BitcoinSPV, + }, + } + + for i := range Hash256FromHexPass { + digest, err := Hash256DigestFromHex(Hash256FromHexPass[i].Input) + assert.Nil(t, err) + assert.Equal(t, digest, Hash256FromHexPass[i].Output) + } + for i := range Hash256FromHexFail { + _, err := Hash256DigestFromHex(Hash256FromHexFail[i].Input) + assert.Equal(t, Hash256FromHexFail[i].Err, err.Code()) + } +} From 493a8cf05e2ae36836929cb795ca1e0b1f4a75b2 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Thu, 5 Dec 2019 12:33:43 -0800 Subject: [PATCH 049/148] WIP: Cli rest messages (#18) * feature: add ingestHeaderChain cli and rest commands * bug: error checking and usage notes for ingestheaders cli command --- golang/x/relay/alias.go | 10 ++++-- golang/x/relay/client/cli/tx.go | 53 +++++++++++++++++++++++++----- golang/x/relay/client/rest/rest.go | 3 +- golang/x/relay/client/rest/tx.go | 49 ++++++++++++++++++++++++++- golang/x/relay/types/codec.go | 5 ++- golang/x/relay/types/msgs.go | 43 ------------------------ 6 files changed, 107 insertions(+), 56 deletions(-) diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index c58d610b..11643a1e 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -19,8 +19,12 @@ var ( NewKeeper = keeper.NewKeeper // NewQuerier is what is says on the tin NewQuerier = keeper.NewQuerier - // NewMsgSetLink is what is says on the tin - NewMsgSetLink = types.NewMsgSetLink + // NewMsgIngestHeaderChain is what is says on the tin + NewMsgIngestHeaderChain = types.NewMsgIngestHeaderChain + // NewMsgIngestDifficultyChange is what is says on the tin + NewMsgIngestDifficultyChange = types.NewMsgIngestDifficultyChange + // NewMsgMarkNewHeaviest is what is says on the tin + NewMsgMarkNewHeaviest = types.NewMsgMarkNewHeaviest // RegisterCodec is what is says on the tin RegisterCodec = types.RegisterCodec // ModuleCdc is what is says on the tin @@ -31,6 +35,8 @@ type ( // Keeper is what is says on the tin Keeper = keeper.Keeper + // TODO: add query structs here + // Hash256Digest 32-byte double-sha2 digest Hash256Digest = types.Hash256Digest diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 54c59cba..5e8eae67 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -1,6 +1,7 @@ package cli import ( + "encoding/json" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -9,6 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -23,31 +25,66 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { } relayTxCmd.AddCommand(client.PostCommands( - GetCmdSetLink(cdc), + GetCmdIngestHeaderChain(cdc), )...) return relayTxCmd } -// GetCmdSetLink is the CLI command for sending a SetLink transaction -func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { +// GetCmdIngestHeaderChain creates a CLI command to ingest a header chain +func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ - Use: "setlink [hex header] [signer address]", - Short: "Set a link", - Args: cobra.ExactArgs(2), + Use: "ingestheaders ", + Short: "ingest a set of headers", + Long: "Ingest a set of headers. The headers must be in order, and the header immediately before the first must already be known to the relay", + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - addr := sdk.AccAddress(args[1]) - msg := types.NewMsgSetLink(addr, args[0], cliCtx.GetFromAddress()) + // note: unmarshalling json here is undesirable, but necessary + // for MarkNewHeaviest we want to accept text instead of json + var headers []types.BitcoinHeader + jsonErr := json.Unmarshal([]byte(args[0]), &headers) + if jsonErr != nil { + return jsonErr + } + + msg := types.NewMsgIngestHeaderChain( + cliCtx.GetFromAddress(), + headers, + ) err := msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + }, } } + +// // GetCmdSetLink is the CLI command for sending a SetLink transaction +// func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { +// return &cobra.Command{ +// Use: "setlink [hex header] [signer address]", +// Short: "Set a link", +// Args: cobra.ExactArgs(2), +// RunE: func(cmd *cobra.Command, args []string) error { +// cliCtx := context.NewCLIContext().WithCodec(cdc) +// +// txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) +// +// addr := sdk.AccAddress(args[1]) +// msg := types.NewMsgSetLink(addr, args[0], cliCtx.GetFromAddress()) +// err := msg.ValidateBasic() +// if err != nil { +// return err +// } +// +// return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) +// }, +// } +// } diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index 570ec33a..37a75a1d 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -12,7 +12,8 @@ import ( func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) { s := r.PathPrefix(fmt.Sprintf("/%s", storeName)).Subrouter() - // r.HandleFunc(fmt.Sprintf("/%s/link", storeName), setLinkHandler(cliCtx)).Methods("POST") + // add new tx msg routes here + s.HandleFunc("/ingestheaderchain", ingestHeaderChainHandler(cliCtx)).Methods("POST") // add new query routes below // {} denotes variable parts of the url route diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 30baa05e..52076961 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -1,6 +1,16 @@ package rest -// +import ( + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + + "github.com/cosmos/cosmos-sdk/types/rest" + "github.com/summa-tx/relays/golang/x/relay/types" +) + // type setLinkReq struct { // BaseReq rest.BaseReq `json:"base_req"` // Header string `json:"header"` @@ -35,3 +45,40 @@ package rest // utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) // } // } + +type ingestHeaderChainReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Headers []types.BitcoinHeader `json:"headers"` + Sender string `json:"sender"` +} + +func ingestHeaderChainHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req ingestHeaderChainReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgIngestHeaderChain(addr, req.Headers) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} diff --git a/golang/x/relay/types/codec.go b/golang/x/relay/types/codec.go index dd2743c0..9767cee9 100644 --- a/golang/x/relay/types/codec.go +++ b/golang/x/relay/types/codec.go @@ -13,5 +13,8 @@ func init() { // RegisterCodec registers concrete types on the Amino codec func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgSetLink{}, "relay/SetLink", nil) + cdc.RegisterConcrete(MsgIngestHeaderChain{}, "relay/IngestHeaderChain", nil) + cdc.RegisterConcrete(MsgIngestDifficultyChange{}, "relay/IngestDifficultyChange", nil) + cdc.RegisterConcrete(MsgMarkNewHeaviest{}, "relay/MarkNewHeaviest", nil) + } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index b2c8f29a..9bdd637d 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -1,55 +1,12 @@ package types import ( - "encoding/hex" - sdk "github.com/cosmos/cosmos-sdk/types" ) // RouterKey is a name for the router const RouterKey = ModuleName // this was defined in your key.go file -/***** SetLink *****/ - -// MsgSetLink defines a SetLink message -type MsgSetLink struct { - Signer sdk.AccAddress `json:"signer"` - Header string `json:"header"` -} - -// NewMsgSetLink is a constructor function for MsgSetLink -func NewMsgSetLink(address sdk.AccAddress, header string, owner sdk.AccAddress) MsgSetLink { - return MsgSetLink{ - address, - header, - } -} - -// GetSigners defines whose signature is required -func (msg MsgSetLink) GetSigners() []sdk.AccAddress { - return []sdk.AccAddress{msg.Signer} -} - -// Type should return the action -func (msg MsgSetLink) Type() string { return "set_link" } - -// ValidateBasic runs stateless checks on the message -func (msg MsgSetLink) ValidateBasic() sdk.Error { - b, err := hex.DecodeString(msg.Header) - if err != nil || len(b) != 80 { - return FromBTCSPVError(DefaultCodespace, err) - } - return nil -} - -// GetSignBytes encodes the message for signing -func (msg MsgSetLink) GetSignBytes() []byte { - return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) -} - -// Route should return the name of the module -func (msg MsgSetLink) Route() string { return RouterKey } - /***** IngestHeaderChain *****/ // MsgIngestHeaderChain defines a IngestHeaderChain message From daa35e03abd826d49940e2c7111770f598d36c03 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Thu, 2 Jan 2020 18:08:36 -0700 Subject: [PATCH 050/148] WIP: Keeper tests (#19) * TestIngestHeaders passing * TestIngestDifficultyChange passing * TestIngestHeaderChain passing * TestEmitExtension passing * GetBestKnownDigest, GetLastReorgLCA tests * TestGetLink passing * TestFindAncestor, TestIsAncestor passing * updated gitignore * updated json, added structs for chain vectors * Test HandleMsgIngestHeaderChain and HandleMsgIngestDifficultyChange * added TestQueryIsAncestor * added TestQueryGetRelayGenesis * added TestQueryGetLastReorgLCA and TestQueryFindAncestor * added more coverage in querier.go * fixed horrendous solidity test code, added error message handling function for tests, started on IsMostRecentCA test * FINALLY finished TestIsMostRecentCommonAncestor * added TestHeaviestFromAncestor * added TestMarkNewHeaviest * added TestQueryHeaviestFromAncestor and TestQueryIsMostRecentCA * added TestHandleMarkNewHeaviest * cleaned up test vectors * fixed test vectors * updated links tests with test vectors * added more test coverage in querier_test * covered a few more test cases --- golang/.gitignore | 2 + golang/x/relay/keeper/chain.go | 13 +- golang/x/relay/keeper/chain_test.go | 181 +++++++ golang/x/relay/keeper/handler_test.go | 108 ++++ golang/x/relay/keeper/headers.go | 18 +- golang/x/relay/keeper/headers_test.go | 103 +++- golang/x/relay/keeper/keeper_test.go | 113 ++++- golang/x/relay/keeper/links_test.go | 57 +++ golang/x/relay/keeper/querier_test.go | 360 ++++++++++++- solidity/package-lock.json | 37 +- solidity/test/Relay.test.js | 59 +-- solidity/test/utils.js | 4 + testVectors.json | 706 +++++++++++++++++++++++++- 13 files changed, 1688 insertions(+), 73 deletions(-) create mode 100644 golang/x/relay/keeper/chain_test.go create mode 100644 golang/x/relay/keeper/handler_test.go create mode 100644 golang/x/relay/keeper/links_test.go diff --git a/golang/.gitignore b/golang/.gitignore index 9ed3b07c..d75238a0 100644 --- a/golang/.gitignore +++ b/golang/.gitignore @@ -1 +1,3 @@ *.test + +/x/relay/keeper/coverage.out \ No newline at end of file diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 3481bd1f..6fee1e4c 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -27,7 +27,6 @@ func (k Keeper) getDigestByStoreKey(ctx sdk.Context, key string) (types.Hash256D } func (k Keeper) setDigestByStoreKey(ctx sdk.Context, key string, digest types.Hash256Digest) { - // TODO: Remove this in favor of Genesis state store := k.getChainStore(ctx) store.Set([]byte(key), digest[:]) } @@ -43,8 +42,8 @@ func (k Keeper) GetBestKnownDigest(ctx sdk.Context) (types.Hash256Digest, sdk.Er } // setLastReorgLCA sets the latest common ancestor of the last reorg -func (k Keeper) setLastReorgLCA(ctx sdk.Context, bestKnown types.Hash256Digest) { - k.setDigestByStoreKey(ctx, types.LastReorgLCAStorage, bestKnown) +func (k Keeper) setLastReorgLCA(ctx sdk.Context, lca types.Hash256Digest) { + k.setDigestByStoreKey(ctx, types.LastReorgLCAStorage, lca) } // GetLastReorgLCA returns the best known digest in the relay @@ -151,15 +150,15 @@ func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, c newBestDigest := btcspv.Hash256(newBest[:]) currentBestDigest := btcspv.Hash256(currentBest[:]) + if !k.HasHeader(ctx, newBestDigest) { + return types.ErrUnknownBlock(types.DefaultCodespace) + } + knownBestDigest, err := k.GetBestKnownDigest(ctx) if err != nil || currentBestDigest != knownBestDigest { return types.ErrNotBestKnown(types.DefaultCodespace) } - if !k.HasHeader(ctx, newBestDigest) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } - if !k.IsMostRecentCommonAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) { return types.ErrNotHeaviestAncestor(types.DefaultCodespace) } diff --git a/golang/x/relay/keeper/chain_test.go b/golang/x/relay/keeper/chain_test.go new file mode 100644 index 00000000..4c7b6626 --- /dev/null +++ b/golang/x/relay/keeper/chain_test.go @@ -0,0 +1,181 @@ +package keeper + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func (s *KeeperSuite) TestEmitReorg() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + s.Keeper.emitReorg(s.Context, headers[0].HashLE, headers[1].HashLE, headers[2].HashLE) + + events := s.Context.EventManager().Events() + e := events[0] + s.Equal(e.Type, "reorg") +} + +func (s *KeeperSuite) TestGetDigestByStoreKey() { + wrongLenDigest := bytes.Repeat([]byte{0}, 31) + key := "bad-digest" + + store := s.Keeper.getChainStore(s.Context) + store.Set([]byte(key), wrongLenDigest) + + _, err := s.Keeper.getDigestByStoreKey(s.Context, key) + s.Equal(sdk.CodeType(105), err.Code()) +} + +func (s *KeeperSuite) TestGetBestKnownDigest() { + digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].HashLE + s.Keeper.setBestKnownDigest(s.Context, digest) + bestKnown, _ := s.Keeper.GetBestKnownDigest(s.Context) + s.Equal(digest, bestKnown) +} + +func (s *KeeperSuite) TestGetLastReorgLCA() { + digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].HashLE + s.Keeper.setLastReorgLCA(s.Context, digest) + lca, _ := s.Keeper.GetLastReorgLCA(s.Context) + s.Equal(digest, lca) +} + +func (s *KeeperSuite) TestIsMostRecentCommonAncestor() { + tv := s.Fixtures.ChainTestCases.IsMostRecentCA + pre := tv.PreRetargetChain + post := tv.PostRetargetChain + + var postWithOrphan []types.BitcoinHeader + postWithOrphan = append(postWithOrphan, post[:len(post)-2]...) + postWithOrphan = append(postWithOrphan, tv.Orphan) + + err := s.Keeper.SetGenesisState(s.Context, tv.Genesis, tv.OldPeriodStart) + s.SDKNil(err) + + err = s.Keeper.IngestHeaderChain(s.Context, pre) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + s.SDKNil(err) + + for i := range tv.TestCases { + isMostRecent := s.Keeper.IsMostRecentCommonAncestor( + s.Context, + tv.TestCases[i].Ancestor, + tv.TestCases[i].Left, + tv.TestCases[i].Right, + tv.TestCases[i].Limit) + s.Equal(tv.TestCases[i].Output, isMostRecent) + } +} + +func (s *KeeperSuite) TestHeaviestFromAncestor() { + tv := s.Fixtures.ChainTestCases.HeaviestFromAncestor + headers := tv.Headers[0:8] + headersWithMain := tv.Headers[0:9] + + var headersWithOrphan []types.BitcoinHeader + headersWithOrphan = append(headersWithOrphan, headers...) + headersWithOrphan = append(headersWithOrphan, tv.Orphan) + + s.Keeper.ingestHeader(s.Context, tv.Genesis) + err := s.Keeper.IngestHeaderChain(s.Context, headersWithMain) + s.SDKNil(err) + err = s.Keeper.IngestHeaderChain(s.Context, headersWithOrphan) + s.SDKNil(err) + + for i := range tv.TestCases { + if tv.TestCases[i].Error == 0 { + heaviest, err := s.Keeper.HeaviestFromAncestor( + s.Context, + tv.TestCases[i].Ancestor, + tv.TestCases[i].CurrentBest, + tv.TestCases[i].NewBest, + tv.TestCases[i].Limit) + s.SDKNil(err) + s.Equal(heaviest, tv.TestCases[i].Output) + } else { + _, err = s.Keeper.HeaviestFromAncestor( + s.Context, + tv.TestCases[i].Ancestor, + tv.TestCases[i].CurrentBest, + tv.TestCases[i].NewBest, + tv.TestCases[i].Limit) + s.Equal(err.Code(), sdk.CodeType(tv.TestCases[i].Error)) + } + } +} + +func (s *KeeperSuite) TestMarkNewHeaviest() { + tv := s.Fixtures.ChainTestCases.IsMostRecentCA + tc := s.Fixtures.ChainTestCases.MarkNewHeaviest.TestCases + pre := tv.PreRetargetChain + post := tv.PostRetargetChain + var postWithOrphan []types.BitcoinHeader + postWithOrphan = append(postWithOrphan, post[:len(post)-2]...) + postWithOrphan = append(postWithOrphan, tv.Orphan) + + err := s.Keeper.SetGenesisState(s.Context, tv.Genesis, tv.OldPeriodStart) + s.SDKNil(err) + + err = s.Keeper.MarkNewHeaviest( + s.Context, + tv.Genesis.HashLE, + pre[0].Raw, + pre[1].Raw, + 10, + ) + s.EqualError(err, 103) + + err = s.Keeper.IngestHeaderChain(s.Context, pre) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + s.SDKNil(err) + + for i := range tc { + if tc[i].Error == 0 { + // updates the best known and emits an event + err = s.Keeper.MarkNewHeaviest( + s.Context, + tc[i].Ancestor, + tc[i].CurrentBest, + tc[i].NewBest, + tc[i].Limit, + ) + events := s.Context.EventManager().Events() + e := events[0] + s.Equal(e.Type, tc[i].Output) + } else { + err = s.Keeper.MarkNewHeaviest( + s.Context, + tc[i].Ancestor, + tc[i].CurrentBest, + tc[i].NewBest, + tc[i].Limit, + ) + s.Equal(err.Code(), sdk.CodeType(tc[i].Error)) + } + } + + // errors if the ancestor is not the heaviest common ancestor + err = s.Keeper.MarkNewHeaviest( + s.Context, + tv.Genesis.HashLE, + tv.Genesis.Raw, + pre[0].Raw, + 10, + ) + s.SDKNil(err) + err = s.Keeper.MarkNewHeaviest( + s.Context, + tv.Genesis.HashLE, + pre[0].Raw, + pre[1].Raw, + 10, + ) + s.Equal(err.Code(), sdk.CodeType(404)) +} diff --git a/golang/x/relay/keeper/handler_test.go b/golang/x/relay/keeper/handler_test.go new file mode 100644 index 00000000..baa190f5 --- /dev/null +++ b/golang/x/relay/keeper/handler_test.go @@ -0,0 +1,108 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func getAccAddress() sdk.AccAddress { + address, _ := sdk.AccAddressFromBech32("cosmos1ay37rp2pc3kjarg7a322vu3sa8j9puah8msyfw") + return address +} + +// Create a bad sdk.msg to pass into TestNewHandler +type MsgBadMessage struct { + Signer sdk.AccAddress `json:"signer"` +} + +func (msg MsgBadMessage) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} +func (msg MsgBadMessage) Type() string { return "bad_message" } +func (msg MsgBadMessage) ValidateBasic() sdk.Error { return nil } +func (msg MsgBadMessage) GetSignBytes() []byte { + return sdk.MustSortJSON(types.ModuleCdc.MustMarshalJSON(msg)) +} +func (msg MsgBadMessage) Route() string { return types.RouterKey } + +func (s *KeeperSuite) TestNewHandler() { + handler := NewHandler(s.Keeper) + + badMsg := MsgBadMessage{ + Signer: getAccAddress(), + } + + res := handler(s.Context, badMsg) + s.Equal(res.Log, "{\"codespace\":\"sdk\",\"code\":6,\"message\":\"Unrecognized relay Msg type: bad_message\"}") +} + +func (s *KeeperSuite) TestHandleMsgIngestHeaderChain() { + testCases := s.Fixtures.HeaderTestCases.ValidateChain + handler := NewHandler(s.Keeper) + + newMsg := types.NewMsgIngestHeaderChain(getAccAddress(), testCases[0].Headers) + + res := handler(s.Context, newMsg) + s.Equal(res.Code, sdk.CodeType(103)) + + s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) + res = handler(s.Context, newMsg) + s.Equal(res.Events[0].Type, "extension") +} + +func (s *KeeperSuite) TestHandleMsgIngestDifficultyChange() { + testCases := s.Fixtures.HeaderTestCases.ValidateDiffChange + handler := NewHandler(s.Keeper) + + newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) + + res := handler(s.Context, newMsg) + s.Equal(res.Code, sdk.CodeType(103)) + + s.Keeper.ingestHeader(s.Context, testCases[0].PrevEpochStart) + s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) + res = handler(s.Context, newMsg) + s.Equal(res.Events[0].Type, "extension") +} + +func (s *KeeperSuite) TestHandleMsgMarkNewHeaviest() { + testCases := s.Fixtures.HeaderTestCases.ValidateDiffChange + handler := NewHandler(s.Keeper) + + s.Keeper.ingestHeader(s.Context, testCases[0].PrevEpochStart) + s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) + newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) + res := handler(s.Context, newMsg) + s.Equal(res.Events[0].Type, "extension") +} + +func (s *KeeperSuite) TestHandleMarkNewHeaviest() { + tv := s.Fixtures.ChainTestCases.IsMostRecentCA + pre := tv.PreRetargetChain + post := tv.PostRetargetChain + handler := NewHandler(s.Keeper) + + var postWithOrphan []types.BitcoinHeader + postWithOrphan = append(postWithOrphan, post[:len(post)-2]...) + postWithOrphan = append(postWithOrphan, tv.Orphan) + + err := s.Keeper.SetGenesisState(s.Context, tv.Genesis, tv.OldPeriodStart) + s.SDKNil(err) + + err = s.Keeper.IngestHeaderChain(s.Context, pre) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + s.SDKNil(err) + + // returns correct error + newMsg := types.NewMsgMarkNewHeaviest(getAccAddress(), tv.OldPeriodStart.HashLE, tv.OldPeriodStart.Raw, tv.OldPeriodStart.Raw, 10) + res := handler(s.Context, newMsg) + s.Equal(res.Code, sdk.CodeType(403)) + + // Successfully marks new heaviest + newMsg = types.NewMsgMarkNewHeaviest(getAccAddress(), tv.Genesis.HashLE, tv.Genesis.Raw, pre[0].Raw, 10) + res = handler(s.Context, newMsg) + s.Equal(res.Events[0].Type, "extension") +} diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index a474a925..bb1285f1 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -78,6 +78,7 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead _, err := header.Validate() if err != nil { return types.FromBTCSPVError(types.DefaultCodespace, err) + // return types.FromBTCSPVError(types.DefaultCodespace, errors.New(fmt.Sprintf("%d err: %d", i, err))) } // ensure height changes as expected @@ -107,13 +108,9 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead } func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { - if !k.HasHeader(ctx, headers[0].PrevHashLE) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } - anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) if err != nil { - return types.ErrUnknownBlock(types.DefaultCodespace) + return err } err = validateHeaderChain(anchor, headers, internal, k.IsMainNet) @@ -158,21 +155,14 @@ func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anc } func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { - if !k.HasHeader(ctx, prevEpochStartLE) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } - if !k.HasHeader(ctx, headers[0].PrevHashLE) { - return types.ErrUnknownBlock(types.DefaultCodespace) - } - // Find the anchor in our store prevEpochStart, err := k.GetHeader(ctx, prevEpochStartLE) if err != nil { - return types.ErrUnknownBlock(types.DefaultCodespace) + return err } anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) if err != nil { - return types.ErrUnknownBlock(types.DefaultCodespace) + return err } err = validateDifficultyChange(headers, prevEpochStart, anchor) diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 9561410e..e3099278 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -1,5 +1,24 @@ package keeper +import "github.com/cosmos/cosmos-sdk/types" + +func (s *KeeperSuite) TestGetHeader() { + // errors if header is not found + header := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0] + _, err := s.Keeper.GetHeader(s.Context, header.HashLE) + s.Equal(types.CodeType(103), err.Code()) +} + +func (s *KeeperSuite) TestEmitExtension() { + // tests extension was emitted successfully + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + s.Keeper.emitExtension(s.Context, headers[0], headers[1]) + + events := s.Context.EventManager().Events() + e := events[0] + s.Equal(e.Type, "extension") +} + func (s *KeeperSuite) TestValidateHeaderChain() { cases := s.Fixtures.HeaderTestCases.ValidateChain @@ -7,7 +26,7 @@ func (s *KeeperSuite) TestValidateHeaderChain() { err := validateHeaderChain(tc.Anchor, tc.Headers, tc.Internal, tc.IsMainnet) if tc.Output == 0 { logIfTestCaseError(tc, err) - s.Nil(err) + s.SDKNil(err) } else { s.NotNil(err) s.Equal(tc.Output, err.Code()) @@ -15,6 +34,60 @@ func (s *KeeperSuite) TestValidateHeaderChain() { } } +func (s *KeeperSuite) TestIngestHeaders() { + cases := s.Fixtures.HeaderTestCases.ValidateChain + + // errors if anchor is not found + err := s.Keeper.ingestHeaders(s.Context, cases[0].Headers, cases[0].Internal) + s.Equal(types.CodeType(103), err.Code()) + + for _, tc := range cases { + s.InitTestContext(tc.IsMainnet, false) + s.Keeper.ingestHeader(s.Context, tc.Anchor) + err := s.Keeper.ingestHeaders(s.Context, tc.Headers, tc.Internal) + if tc.Output == 0 { + logIfTestCaseError(tc, err) + s.SDKNil(err) + } else { + s.NotNil(err) + s.Equal(tc.Output, err.Code()) + } + } +} + +func (s *KeeperSuite) TestIngestHeaderChain() { + cases := s.Fixtures.HeaderTestCases.ValidateChain + + for _, tc := range cases { + if tc.Internal == false { + s.InitTestContext(tc.IsMainnet, false) + s.Keeper.ingestHeader(s.Context, tc.Anchor) + err := s.Keeper.IngestHeaderChain(s.Context, tc.Headers) + if tc.Output == 0 { + logIfTestCaseError(tc, err) + s.SDKNil(err) + } else { + s.NotNil(err) + s.Equal(tc.Output, err.Code()) + } + } + } +} + +// TestIngestHeader tests ingestHeader, HasHeader, and GetHeader +func (s *KeeperSuite) TestIngestHeader() { + cases := s.Fixtures.HeaderTestCases.ValidateChain + + for _, tc := range cases { + s.Keeper.ingestHeader(s.Context, tc.Headers[0]) + hasHeader := s.Keeper.HasHeader(s.Context, tc.Headers[0].HashLE) + s.Equal(hasHeader, true) + header, err := s.Keeper.GetHeader(s.Context, tc.Headers[0].HashLE) + s.SDKNil(err) + s.Equal(header, tc.Headers[0]) + } +} + func (s *KeeperSuite) TestValidateDifficultyChange() { cases := s.Fixtures.HeaderTestCases.ValidateDiffChange @@ -22,7 +95,33 @@ func (s *KeeperSuite) TestValidateDifficultyChange() { err := validateDifficultyChange(tc.Headers, tc.PrevEpochStart, tc.Anchor) if tc.Output == 0 { logIfTestCaseError(tc, err) - s.Nil(err) + s.SDKNil(err) + } else { + s.NotNil(err) + s.Equal(tc.Output, err.Code()) + } + } +} + +func (s *KeeperSuite) TestIngestDifficultyChange() { + cases := s.Fixtures.HeaderTestCases.ValidateDiffChange + + // errors if PrevEpochStart is not found + err := s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.HashLE, cases[0].Headers) + s.Equal(types.CodeType(103), err.Code()) + + // errors if anchor is not found + s.Keeper.ingestHeader(s.Context, cases[0].PrevEpochStart) + err = s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.HashLE, cases[0].Headers) + s.Equal(types.CodeType(103), err.Code()) + + for _, tc := range cases { + s.Keeper.ingestHeader(s.Context, tc.PrevEpochStart) + s.Keeper.ingestHeader(s.Context, tc.Anchor) + err := s.Keeper.IngestDifficultyChange(s.Context, tc.PrevEpochStart.HashLE, tc.Headers) + if tc.Output == 0 { + logIfTestCaseError(tc, err) + s.SDKNil(err) } else { s.NotNil(err) s.Equal(tc.Output, err.Code()) diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index e2332fee..28587b1e 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -2,6 +2,7 @@ package keeper import ( "encoding/json" + "fmt" "io/ioutil" "log" "os" @@ -32,10 +33,89 @@ type Case struct { Comment string `json:"comment"` } -type LinkTest struct{} +/***** LINK TEST CASES *****/ +type IsAncestorTestCase struct { + Digest types.Hash256Digest `json:"digest"` + Ancestor types.Hash256Digest `json:"ancestor"` + Limit uint32 `json:"limit"` + Output bool `json:"output"` +} + +type IsAncestor struct { + TestCases []IsAncestorTestCase `json:"testCases"` +} + +type FindAncestorTestCase struct { + Digest types.Hash256Digest `json:"digest"` + Offset uint32 `json:"offset"` + Error int `json:"error"` + Output types.Hash256Digest `json:"output"` +} + +type FindAncestor struct { + TestCases []FindAncestorTestCase `json:"testCases"` +} -type ChainTest struct{} +type LinkTestCases struct { + IsAncestor IsAncestor `json:"isAncestor"` + FindAncestor FindAncestor `json:"findAncestor"` +} +/***** CHAIN TEST CASES *****/ +type MostRecentCATestCase struct { + Ancestor types.Hash256Digest `json:"ancestor"` + Left types.Hash256Digest `json:"left"` + Right types.Hash256Digest `json:"right"` + Limit uint32 `json:"limit"` + Output bool `json:"output"` +} + +type IsMostRecentCA struct { + Orphan types.BitcoinHeader `json:"orphan"` + OldPeriodStart types.BitcoinHeader `json:"oldPeriodStart"` + Genesis types.BitcoinHeader `json:"genesis"` + PreRetargetChain []types.BitcoinHeader `json:"preRetargetChain"` + PostRetargetChain []types.BitcoinHeader `json:"postRetargetChain"` + TestCases []MostRecentCATestCase `json:"testCases"` +} + +type HeaviestTestCase struct { + Ancestor types.Hash256Digest `json:"ancestor"` + CurrentBest types.Hash256Digest `json:"currentBest"` + NewBest types.Hash256Digest `json:"newBest"` + Limit uint32 `json:"limit"` + Error int `json:"error"` + Output types.Hash256Digest `json:"output"` +} + +type HeaviestFromAncestor struct { + Orphan types.BitcoinHeader `json:"orphan"` + BadHeader types.BitcoinHeader `json:"badHeader"` + Genesis types.BitcoinHeader `json:"genesis"` + Headers []types.BitcoinHeader `json:"headers"` + TestCases []HeaviestTestCase `json:"testCases"` +} + +type NewHeaviestTestCase struct { + Ancestor types.Hash256Digest `json:"ancestor"` + CurrentBest types.RawHeader `json:"currentBest"` + NewBest types.RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` + Error int `json:"error"` + Output string `json:"output"` +} + +type MarkNewHeaviest struct { + TestCases []NewHeaviestTestCase `json:"testCases"` +} + +type ChainTestCases struct { + IsMostRecentCA IsMostRecentCA `json:"isMostRecentCommonAncestor"` + HeaviestFromAncestor HeaviestFromAncestor `json:"heaviestFromAncestor"` + MarkNewHeaviest MarkNewHeaviest `json:"markNewHeaviest"` +} + +/***** HEADER TEST CASES *****/ type IngestCase struct { Case Headers []types.BitcoinHeader `json:"headers"` @@ -66,10 +146,11 @@ type HeaderTestCases struct { CompareTargets []CompareCase `json:"compareTargets"` } +/***** KEEPER TEST CASES *****/ type KeeperTestCases struct { - LinkTestCases []LinkTest `json:"link"` + LinkTestCases LinkTestCases `json:"link"` HeaderTestCases HeaderTestCases `json:"header"` - ChainTestCases []ChainTest `json:"chain"` + ChainTestCases ChainTestCases `json:"chain"` } type KeeperSuite struct { @@ -149,6 +230,22 @@ func TestKeeper(t *testing.T) { suite.Run(t, keeperSuite) } +func (suite *KeeperSuite) SDKNil(e sdk.Error) { + var msg string + if e != nil { + msg = e.Error() + } + suite.Nil(e, msg) +} + +func (suite *KeeperSuite) EqualError(e sdk.Error, code int) { + var msg string + if e.Code() != sdk.CodeType(code) { + msg = fmt.Sprintf("%sExpected: %d\n", e.Error(), code) + } + suite.Equal(e.Code(), sdk.CodeType(code), msg) +} + func (s *KeeperSuite) TestGetPrefixStore() { prefStore := s.Keeper.getPrefixStore(s.Context, "toast-") store := s.Context.KVStore(s.Keeper.storeKey) @@ -165,18 +262,18 @@ func (s *KeeperSuite) TestSetGenesisState() { genesis := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].Anchor epochStart := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].PrevEpochStart err := s.Keeper.SetGenesisState(s.Context, genesis, epochStart) - s.Nil(err) + s.SDKNil(err) gen, err := s.Keeper.GetRelayGenesis(s.Context) - s.Nil(err) + s.SDKNil(err) s.Equal(genesis.HashLE, gen) lca, err := s.Keeper.GetLastReorgLCA(s.Context) - s.Nil(err) + s.SDKNil(err) s.Equal(genesis.HashLE, lca) best, err := s.Keeper.GetBestKnownDigest(s.Context) - s.Nil(err) + s.SDKNil(err) s.Equal(genesis.HashLE, best) err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) diff --git a/golang/x/relay/keeper/links_test.go b/golang/x/relay/keeper/links_test.go new file mode 100644 index 00000000..0afceb83 --- /dev/null +++ b/golang/x/relay/keeper/links_test.go @@ -0,0 +1,57 @@ +package keeper + +import "github.com/cosmos/cosmos-sdk/types" + +func (s *KeeperSuite) TestGetLink() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + parent := headers[0] + child := headers[1] + + // stores and retrieves link + s.Keeper.setLink(s.Context, child) + hasHeader := s.Keeper.hasLink(s.Context, child.HashLE) + s.Equal(true, hasHeader) + getHeader := s.Keeper.getLink(s.Context, child.HashLE) + s.Equal(getHeader, parent.HashLE) +} + +// TODO: Add test cases: add to JSON, loop over +func (s *KeeperSuite) TestFindAncestor() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + anchor := s.Fixtures.HeaderTestCases.ValidateChain[0].Anchor + tc := s.Fixtures.LinkTestCases.FindAncestor.TestCases + + // errors if link is not found + _, err := s.Keeper.FindAncestor(s.Context, tc[0].Digest, tc[0].Offset) + s.Equal(err.Code(), types.CodeType(tc[0].Error)) + + s.Keeper.ingestHeader(s.Context, anchor) + s.Keeper.IngestHeaderChain(s.Context, headers) + + for i := 1; i < len(tc); i++ { + if tc[i].Error == 0 { + // successfully retrieves ancestor + ancestor, err := s.Keeper.FindAncestor(s.Context, tc[i].Digest, tc[i].Offset) + s.SDKNil(err) + s.Equal(tc[i].Output, ancestor) + } else { + // errors if link is not found + _, err = s.Keeper.FindAncestor(s.Context, tc[i].Digest, tc[i].Offset) + s.Equal(err.Code(), types.CodeType(tc[i].Error)) + } + } +} + +func (s *KeeperSuite) TestIsAncestor() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + anchor := s.Fixtures.HeaderTestCases.ValidateChain[0].Anchor + tc := s.Fixtures.LinkTestCases.IsAncestor.TestCases + + s.Keeper.ingestHeader(s.Context, anchor) + s.Keeper.IngestHeaderChain(s.Context, headers) + + for i := range tc { + isAncestor := s.Keeper.IsAncestor(s.Context, tc[i].Digest, tc[i].Ancestor, tc[i].Limit) + s.Equal(tc[i].Output, isAncestor) + } +} diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index 9a779e11..e58df838 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -1,8 +1,11 @@ package keeper import ( + "encoding/json" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" + abci "github.com/tendermint/tendermint/abci/types" ) func (suite *KeeperSuite) TestDecodeUint32FromPath() { @@ -44,7 +47,7 @@ func (suite *KeeperSuite) TestDecodeUint32FromPath() { index := DecodeUintPass[i].Idx limit := DecodeUintPass[i].DefaultLimit num, err := decodeUint32FromPath(path, index, limit) - suite.Nil(err) + suite.SDKNil(err) suite.Equal(num, DecodeUintPass[i].Output) } for i := range DecodeUintFail { @@ -55,3 +58,358 @@ func (suite *KeeperSuite) TestDecodeUint32FromPath() { suite.Equal(DecodeUintFail[i].Err, err.Code()) } } + +func (s *KeeperSuite) TestNewQuerier() { + querier := NewQuerier(s.Keeper) + + // Set up neccessary params with a bad path + path := []string{"badpath"} + + req := abci.RequestQuery{ + Path: "custom/relay/badpath", + Data: []byte{}, + } + + // Test that NewQuerier errors when given a bad path + _, err := querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(6)) +} + +func (s *KeeperSuite) TestQueryIsAncestor() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + anchor := s.Fixtures.HeaderTestCases.ValidateChain[0].Anchor + querier := NewQuerier(s.Keeper) + + s.Keeper.ingestHeader(s.Context, anchor) + s.Keeper.IngestHeaderChain(s.Context, headers) + + params := types.QueryParamsIsAncestor{ + DigestLE: headers[4].HashLE, + ProspectiveAncestor: headers[1].HashLE, + Limit: 15, + } + marshalledParams, marshalErr := json.Marshal(params) + s.Nil(marshalErr) + + path := []string{"isancestor"} + + req := abci.RequestQuery{ + Path: "custom/relay/isancestor", + Data: marshalledParams, + } + + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + var result types.QueryResIsAncestor + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, true) + + // If Limit is 0, it will use default limit + params = types.QueryParamsIsAncestor{ + DigestLE: headers[4].HashLE, + ProspectiveAncestor: headers[1].HashLE, + Limit: 0, + } + marshalledParams, marshalErr = json.Marshal(params) + s.Nil(marshalErr) + req = abci.RequestQuery{ + Path: "custom/relay/isancestor", + Data: marshalledParams, + } + + res, err = querier(s.Context, path, req) + s.SDKNil(err) + + unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, true) + + // Test unmarshall error + req = abci.RequestQuery{ + Path: "custom/relay/isancestor", + Data: []byte{1, 1, 1, 1}, + } + + _, err = querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(1)) +} + +func (s *KeeperSuite) TestQueryGetRelayGenesis() { + genesis := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].Anchor + epochStart := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].PrevEpochStart + // Make a new querier + querier := NewQuerier(s.Keeper) + + path := []string{"getrelaygenesis"} + + req := abci.RequestQuery{ + Path: "custom/relay/getrelaygenesis", + Data: []byte{}, + } + + // Test that GetRelayGenesis errors if RelayGenesis is not found + _, err := querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(105)) + + // Set Genesis state + err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) + s.SDKNil(err) + + // Use querier handler to get RelayGenesis + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + // Unmarshall the result and test + var result types.QueryResGetRelayGenesis + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, genesis.HashLE) +} + +func (s *KeeperSuite) TestQueryGetLastReorgLCA() { + genesis := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].Anchor + epochStart := s.Fixtures.HeaderTestCases.ValidateDiffChange[0].PrevEpochStart + querier := NewQuerier(s.Keeper) + + path := []string{"getlastreorglca"} + + req := abci.RequestQuery{ + Path: "custom/relay/getlastreorglca", + Data: []byte{}, + } + + // Test that it errors if it doesn't find LastReorgLCA + _, err := querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(105)) + + setStateErr := s.Keeper.SetGenesisState(s.Context, genesis, epochStart) + s.SDKNil(setStateErr) + + res, getLCAErr := querier(s.Context, path, req) + s.SDKNil(getLCAErr) + + var result types.QueryResGetLastReorgLCA + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, genesis.HashLE) +} + +func (s *KeeperSuite) TestQueryFindAncestor() { + headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers + anchor := s.Fixtures.HeaderTestCases.ValidateChain[0].Anchor + querier := NewQuerier(s.Keeper) + + params := types.QueryParamsFindAncestor{ + DigestLE: headers[4].HashLE, + Offset: 2, + } + marshalledParams, marshalErr := json.Marshal(params) + s.Nil(marshalErr) + + path := []string{"findancestor"} + req := abci.RequestQuery{ + Path: "custom/relay/findancestor", + Data: marshalledParams, + } + + // Test that it errors if ancestor is not found + _, findAncestorErr := querier(s.Context, path, req) + s.Equal(findAncestorErr.Code(), sdk.CodeType(103)) + + // initialize data + s.Keeper.ingestHeader(s.Context, anchor) + s.Keeper.IngestHeaderChain(s.Context, headers) + + // test that it retrieves the correct ancestor + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + var result types.QueryResFindAncestor + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + // s.SDKNil(unmarshallErr) + s.Nil(unmarshallErr) + s.Equal(result.Res, headers[2].HashLE) + + // Test unmarshall error + req = abci.RequestQuery{ + Path: "custom/relay/findancestor", + Data: []byte{1, 1, 1, 1}, + } + + _, err = querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(1)) +} + +func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { + tv := s.Fixtures.ChainTestCases.HeaviestFromAncestor + headers := tv.Headers[0:8] + headersWithMain := tv.Headers[0:9] + querier := NewQuerier(s.Keeper) + + var headersWithOrphan []types.BitcoinHeader + headersWithOrphan = append(headersWithOrphan, headers...) + headersWithOrphan = append(headersWithOrphan, tv.Orphan) + + s.Keeper.ingestHeader(s.Context, tv.Genesis) + err := s.Keeper.IngestHeaderChain(s.Context, headersWithMain) + s.SDKNil(err) + err = s.Keeper.IngestHeaderChain(s.Context, headersWithOrphan) + s.SDKNil(err) + + params := types.QueryParamsHeaviestFromAncestor{ + Ancestor: headers[3].HashLE, + CurrentBest: headers[5].HashLE, + NewBest: headers[4].HashLE, + Limit: 20, + } + marshalledParams, marshalErr := json.Marshal(params) + s.Nil(marshalErr) + + path := []string{"heaviestfromancestor"} + + req := abci.RequestQuery{ + Path: "custom/relay/heaviestfromancestor", + Data: marshalledParams, + } + + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + var result types.QueryResHeaviestFromAncestor + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, headers[5].HashLE) + + // Test that it errors if HeaviestFromAncestorErrors + params = types.QueryParamsHeaviestFromAncestor{ + Ancestor: tv.Headers[10].HashLE, + CurrentBest: headers[3].HashLE, + NewBest: headers[4].HashLE, + Limit: 20, + } + marshalledParams, marshalErr = json.Marshal(params) + s.Nil(marshalErr) + + req = abci.RequestQuery{ + Path: "custom/relay/heaviestfromancestor", + Data: marshalledParams, + } + + res, err = querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(103)) + + // Test that default limit is used if limit is set to zero + params = types.QueryParamsHeaviestFromAncestor{ + Ancestor: headers[3].HashLE, + CurrentBest: headers[5].HashLE, + NewBest: headers[4].HashLE, + Limit: 0, + } + marshalledParams, marshalErr = json.Marshal(params) + s.Nil(marshalErr) + + req = abci.RequestQuery{ + Path: "custom/relay/heaviestfromancestor", + Data: marshalledParams, + } + + res, err = querier(s.Context, path, req) + s.SDKNil(err) + + unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, headers[5].HashLE) + + // Test unmarshall error + req = abci.RequestQuery{ + Path: "custom/relay/heaviestfromancestor", + Data: []byte{1, 1, 1, 1}, + } + + _, err = querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(1)) +} + +func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { + tv := s.Fixtures.ChainTestCases.IsMostRecentCA + pre := tv.PreRetargetChain + post := tv.PostRetargetChain + querier := NewQuerier(s.Keeper) + + var postWithOrphan []types.BitcoinHeader + postWithOrphan = append(postWithOrphan, post[:len(post)-2]...) + postWithOrphan = append(postWithOrphan, tv.Orphan) + + err := s.Keeper.SetGenesisState(s.Context, tv.Genesis, tv.OldPeriodStart) + s.SDKNil(err) + + err = s.Keeper.IngestHeaderChain(s.Context, pre) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + s.SDKNil(err) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + s.SDKNil(err) + + params := types.QueryParamsIsMostRecentCommonAncestor{ + Ancestor: post[2].HashLE, + Left: post[3].HashLE, + Right: post[2].HashLE, + Limit: 5, + } + marshalledParams, marshalErr := json.Marshal(params) + s.Nil(marshalErr) + + path := []string{"ismostrecentcommonancestor"} + + req := abci.RequestQuery{ + Path: "custom/relay/ismostrecentcommonancestor", + Data: marshalledParams, + } + + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + var result types.QueryResIsMostRecentCommonAncestor + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, true) + + // Test that it looks up the default limit if limit is set to zero + params = types.QueryParamsIsMostRecentCommonAncestor{ + Ancestor: post[2].HashLE, + Left: post[3].HashLE, + Right: post[2].HashLE, + Limit: 0, + } + marshalledParams, marshalErr = json.Marshal(params) + s.Nil(marshalErr) + + req = abci.RequestQuery{ + Path: "custom/relay/ismostrecentcommonancestor", + Data: marshalledParams, + } + + res, err = querier(s.Context, path, req) + s.SDKNil(err) + + unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(result.Res, true) + + // Test unmarshall error + req = abci.RequestQuery{ + Path: "custom/relay/ismostrecentcommonancestor", + Data: []byte{1, 1, 1, 1}, + } + + _, err = querier(s.Context, path, req) + s.Equal(err.Code(), sdk.CodeType(1)) +} diff --git a/solidity/package-lock.json b/solidity/package-lock.json index 30061010..a801512f 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -1,5 +1,5 @@ { - "name": "summa-relay-sol", + "name": "@summa-tx/relay-sol", "version": "1.0.0", "lockfileVersion": 1, "requires": true, @@ -9812,6 +9812,41 @@ "underscore": "1.9.1", "web3-core-helpers": "1.2.1", "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true + }, + "websocket": { + "version": "github:web3-js/WebSocket-Node#905deb4812572b344f5801f8c9ce8bb02799d82e", + "from": "github:web3-js/WebSocket-Node#polyfill/globalThis", + "dev": true, + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } + } } }, "web3-shh": { diff --git a/solidity/test/Relay.test.js b/solidity/test/Relay.test.js index 8d2bb424..81160f7e 100644 --- a/solidity/test/Relay.test.js +++ b/solidity/test/Relay.test.js @@ -466,13 +466,10 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const preHex = PRE_CHAIN.map(header => header.hex); - const pre = utils.concatenateHexStrings(preHex); - const postHex = POST_CHAIN.map(header => header.hex); - const post = utils.concatenateHexStrings(postHex.slice(0, -2)); - const postWithOrphan = utils.concatenateHexStrings([post, orphan.hex]); - const lastTwo = POST_CHAIN.slice(-2); - const postWithoutOrphan = utils.concatenateHexStrings([post, lastTwo[0].hex, lastTwo[1].hex]); + const pre = utils.concatenateHeadersHexes(PRE_CHAIN) + const post = utils.concatenateHeadersHexes(POST_CHAIN) + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { instance = await Relay.new( @@ -486,12 +483,12 @@ contract('Relay', async () => { ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], - postWithoutOrphan + PRE_CHAIN[PRE_CHAIN.length - 1].hex, + post ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], + PRE_CHAIN[PRE_CHAIN.length - 1].hex, postWithOrphan ); }); @@ -500,13 +497,13 @@ contract('Relay', async () => { let res = await instance.heaviestFromAncestor.call( REORG_AND_RETARGET_CHAIN.genesis.digest_le, orphan.hex, - preHex[3] + PRE_CHAIN[3].hex ); assert.equal(res, orphan.digest_le); res = await instance.heaviestFromAncestor.call( REORG_AND_RETARGET_CHAIN.genesis.digest_le, - preHex[3], + PRE_CHAIN[3].hex, orphan.hex ); assert.equal(res, orphan.digest_le); @@ -516,13 +513,13 @@ contract('Relay', async () => { let res = await instance.heaviestFromAncestor.call( REORG_AND_RETARGET_CHAIN.genesis.digest_le, orphan.hex, - postHex[3] + POST_CHAIN[3].hex ); assert.equal(res, orphan.digest_le); res = await instance.heaviestFromAncestor.call( REORG_AND_RETARGET_CHAIN.genesis.digest_le, - postHex[3], + POST_CHAIN[3].hex, orphan.hex ); assert.equal(res, orphan.digest_le); @@ -534,13 +531,10 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const preHex = PRE_CHAIN.map(header => header.hex); - const pre = utils.concatenateHexStrings(preHex); - const postHex = POST_CHAIN.map(header => header.hex); - const post = utils.concatenateHexStrings(postHex.slice(0, -2)); - const postWithOrphan = utils.concatenateHexStrings([post, orphan.hex]); - const lastTwo = POST_CHAIN.slice(-2); - const postWithoutOrphan = utils.concatenateHexStrings([post, lastTwo[0].hex, lastTwo[1].hex]); + const pre = utils.concatenateHeadersHexes(PRE_CHAIN) + const post = utils.concatenateHeadersHexes(POST_CHAIN) + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { instance = await Relay.new( @@ -554,12 +548,12 @@ contract('Relay', async () => { ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], - postWithoutOrphan + PRE_CHAIN[PRE_CHAIN.length - 1].hex, + post ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], + PRE_CHAIN[PRE_CHAIN.length - 1].hex, postWithOrphan ); }); @@ -618,13 +612,10 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const preHex = PRE_CHAIN.map(header => header.hex); - const pre = utils.concatenateHexStrings(preHex); - const postHex = POST_CHAIN.map(header => header.hex); - const post = utils.concatenateHexStrings(postHex.slice(0, -2)); - const postWithOrphan = utils.concatenateHexStrings([post, orphan.hex]); - const lastTwo = POST_CHAIN.slice(-2); - const postWithoutOrphan = utils.concatenateHexStrings([post, lastTwo[0].hex, lastTwo[1].hex]); + const pre = utils.concatenateHeadersHexes(PRE_CHAIN) + const post = utils.concatenateHeadersHexes(POST_CHAIN) + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { instance = await Relay.new( @@ -638,12 +629,12 @@ contract('Relay', async () => { ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], - postWithoutOrphan + PRE_CHAIN[PRE_CHAIN.length - 1].hex, + post ); await instance.addHeadersWithRetarget( REORG_AND_RETARGET_CHAIN.oldPeriodStart.hex, - preHex.slice(-1)[0], + PRE_CHAIN[PRE_CHAIN.length - 1].hex, postWithOrphan ); }); diff --git a/solidity/test/utils.js b/solidity/test/utils.js index dd0e10e1..571ea5ac 100644 --- a/solidity/test/utils.js +++ b/solidity/test/utils.js @@ -12,4 +12,8 @@ module.exports = { } return current; }, + concatenateHeadersHexes: function concatenateHeadersHexes(arr) { + const hexes = arr.map(arr => arr.hex); + return this.concatenateHexStrings(hexes); + } }; diff --git a/testVectors.json b/testVectors.json index d2edc66c..9803bbac 100644 --- a/testVectors.json +++ b/testVectors.json @@ -1,7 +1,42 @@ { - "link": [ - {} - ], + "link": { + "isAncestor": { + "testCases": [{ + "digest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "ancestor": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "limit": 15, + "output": true + }, { + "digest": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "ancestor": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "limit": 15, + "output": false + }, { + "digest": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "ancestor": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "limit": 0, + "output": false + }] + }, + "findAncestor": { + "testCases": [{ + "digest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "offset": 2, + "error": 103, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "digest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "offset": 2, + "error": 0, + "output": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000" + }, { + "digest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "offset": 3, + "error": 103, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }] + } + }, "header": { "validateDifficultyChange": [ { @@ -49,6 +84,160 @@ "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 0 + }, + { + "comment": "invalid anchor", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + } + ], + "prevEpochStart": { + "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", + "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", + "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "height": 304416, + "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", + "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", + "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + }, + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306432, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "output": 301 + }, + { + "comment": "invalid prevEphochStart", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + } + ], + "prevEpochStart": { + "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", + "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", + "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "height": 304417, + "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", + "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", + "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + }, + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "output": 302 + }, + { + "comment": "period header difficulties do not match", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + } + ], + "prevEpochStart": { + "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690538a855d18b189b5e4", + "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", + "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "height": 304416, + "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", + "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", + "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + }, + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "output": 303 + }, + { + "comment": "invalid retarget", + "headers": [ + { + "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5218824d1fac", + "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "height": 306432, + "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", + "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + }, + { + "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", + "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", + "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "height": 306433, + "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", + "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", + "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + } + ], + "prevEpochStart": { + "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", + "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", + "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "height": 304416, + "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", + "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", + "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + }, + "anchor": { + "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", + "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", + "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "height": 306431, + "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", + "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", + "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + }, + "output": 304 } ], "validateHeaderChain": [ @@ -497,7 +686,512 @@ } ] }, - "chain": [ - {} - ] + "chain": { + "isMostRecentCommonAncestor": { + "orphan": { + "raw": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", + "hash": "0x0000000000000000011da79d23e40a8360ed09d7f7e67e85e5fcf14377694df2", + "hash_le": "0xf24d697743f1fce5857ee6f7d709ed60830ae4239da71d010000000000000000", + "height": 437478, + "prevhash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", + "prevhash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "merkle_root": "0x9710937c6624a63be51082c04da9c9278b0350977c41347cff5811e3a4392cc5", + "merkle_root_le": "0xc52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c931097" + }, + "oldPeriodStart": { + "raw": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", + "hash": "0x00000000000000000082ed5c599748dbaf59b084445c3f0c107fef194fe9009b", + "hash_le": "0x9b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", + "height": 435456, + "prevhash": "0x0000000000000000027fd968d41741f31c73c4a3b304472da0165245278e2ea3", + "prevhash_le": "0xa32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000", + "merkle_root": "0x0138c1cf3f774df6793460fd67c0adff82ccb203ca3d73371ad3c24265a48f94", + "merkle_root_le": "0x948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801" + }, + "genesis": { + "raw": "0x00000020d0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f200000000000000000005e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594af3fe1d58d2550418e90ec632", + "hash": "0x00000000000000000073a63eb7045711d8b7d27f2a13948cd70a858980acd416", + "hash_le": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", + "height": 437466, + "prevhash": "0x000000000000000000209f03eb2c9056fd40d957f6e683bbda8d66a5c98cebd0", + "prevhash_le": "0xd0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f20000000000000000000", + "merkle_root": "0x4a59a169cfb7bc8839eca7c4bff6a6afae1c2ce7d14856143019e81ef57c405e", + "merkle_root_le": "0x5e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594a" + }, + "preRetargetChain": [ + { + "raw": "0x0000002016d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f5b001e58d2550418183b1b41", + "hash": "0x0000000000000000033c5bbb5aa43f276e3701168b7e43b40a41dd960101bc58", + "hash_le": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", + "height": 437467, + "prevhash": "0x00000000000000000073a63eb7045711d8b7d27f2a13948cd70a858980acd416", + "prevhash_le": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", + "merkle_root": "0x0fed153bbbb6658eb1c64ca2180d76e3755a4a8e4078902e74bddadbe6563999", + "merkle_root_le": "0x993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f" + }, + { + "raw": "0x0000002058bc010196dd410ab4437e8b1601376e273fa45abb5b3c03000000000000000035092ae7558bab7b92b811bd07bd3a5a3c16a84e37a67e47817e813728f6f5e612011e58d255041805ebb671", + "hash": "0x0000000000000000039c463b6149a95a3ba6e0d39aa90755f56477bbd9711901", + "hash_le": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", + "height": 437468, + "prevhash": "0x0000000000000000033c5bbb5aa43f276e3701168b7e43b40a41dd960101bc58", + "prevhash_le": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", + "merkle_root": "0xe6f5f62837817e81477ea6374ea8163c5a3abd07bd11b8927bab8b55e72a0935", + "merkle_root_le": "0x35092ae7558bab7b92b811bd07bd3a5a3c16a84e37a67e47817e813728f6f5e6" + }, + { + "raw": "0x00000020011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000ea0ac12b8c46d54d1eac6637b916ae7775aad858dc1c8a43cff50c40bb51a3bc2c081e58d255041859d608ec", + "hash": "0x0000000000000000020daace431ae4ab0509e877c069fb35d98a190af3df1473", + "hash_le": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", + "height": 437469, + "prevhash": "0x0000000000000000039c463b6149a95a3ba6e0d39aa90755f56477bbd9711901", + "prevhash_le": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", + "merkle_root": "0xbca351bb400cf5cf438a1cdc58d8aa7577ae16b93766ac1e4dd5468c2bc10aea", + "merkle_root_le": "0xea0ac12b8c46d54d1eac6637b916ae7775aad858dc1c8a43cff50c40bb51a3bc" + }, + { + "raw": "0x000000207314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000d3439b160498d4464fcb6eee7ac75e801933067932f30234e3b924e2671e31a4e1091e58d25504188ccd158e", + "hash": "0x000000000000000003f58f0d85e555bc292b518fa012a90c8b5e45d24993fff2", + "hash_le": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "height": 437470, + "prevhash": "0x0000000000000000020daace431ae4ab0509e877c069fb35d98a190af3df1473", + "prevhash_le": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", + "merkle_root": "0xa4311e67e224b9e33402f33279063319805ec77aee6ecb4f46d49804169b43d3", + "merkle_root_le": "0xd3439b160498d4464fcb6eee7ac75e801933067932f30234e3b924e2671e31a4" + }, + { + "raw": "0x00000020f2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff503000000000000000051aa623be08b19c2a6984e0c229bbffce20673675267eab5bf09a963b0547c673c0c1e58d25504183e20ab5b", + "hash": "0x00000000000000000364a23184b8a2c009d13172094421c22e4d9bc85dcf90a5", + "hash_le": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", + "height": 437471, + "prevhash": "0x000000000000000003f58f0d85e555bc292b518fa012a90c8b5e45d24993fff2", + "prevhash_le": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "merkle_root": "0x677c54b063a909bfb5ea6752677306e2fcbf9b220c4e98a6c2198be03b62aa51", + "merkle_root_le": "0x51aa623be08b19c2a6984e0c229bbffce20673675267eab5bf09a963b0547c67" + } + ], + "postRetargetChain": [ + { + "raw": "0x00000020a590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000a37e2d39aaf31d8bd7c00a07fa9784fee88ab361082f03601119dac4a0d87c8d020d1e58745104189b665b65", + "hash": "0x000000000000000003c75c23f1444676c6832d45badd6ae89b8d1903d92563b7", + "hash_le": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "height": 437472, + "prevhash": "0x00000000000000000364a23184b8a2c009d13172094421c22e4d9bc85dcf90a5", + "prevhash_le": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", + "merkle_root": "0x8d7cd8a0c4da191160032f0861b38ae8fe8497fa070ac0d78b1df3aa392d7ea3", + "merkle_root_le": "0xa37e2d39aaf31d8bd7c00a07fa9784fee88ab361082f03601119dac4a0d87c8d" + }, + { + "raw": "0x00000020b76325d903198d9be86addba452d83c6764644f1235cc70300000000000000001d9e6cc2b0871a8f88db857db870dfa7000f80f3c89c7a0eaaa925974fc392459e111e5874510418f4dcbaed", + "hash": "0x0000000000000000006084d9f6fb8b81349fcde72e85ab5e39e9d0b728f32b61", + "hash_le": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", + "height": 437473, + "prevhash": "0x000000000000000003c75c23f1444676c6832d45badd6ae89b8d1903d92563b7", + "prevhash_le": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "merkle_root": "0x4592c34f9725a9aa0e7a9cc8f3800f00a7df70b87d85db888f1a87b0c26c9e1d", + "merkle_root_le": "0x1d9e6cc2b0871a8f88db857db870dfa7000f80f3c89c7a0eaaa925974fc39245" + }, + { + "raw": "0x00000020612bf328b7d0e9395eab852ee7cd9f34818bfbf6d984600000000000000000001698022f9aeaccae63250f24ad66093504b088686d2e307bf7653c700b7e23d1e2111e5874510418914866a7", + "hash": "0x000000000000000003a15741d88dcbdab6c39b17dddf4829b6b8b509313bb14d", + "hash_le": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "height": 437474, + "prevhash": "0x0000000000000000006084d9f6fb8b81349fcde72e85ab5e39e9d0b728f32b61", + "prevhash_le": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", + "merkle_root": "0xd1237e0b703c65f77b302e6d6888b004350966ad240f2563aeccea9a2f029816", + "merkle_root_le": "0x1698022f9aeaccae63250f24ad66093504b088686d2e307bf7653c700b7e23d1" + }, + { + "raw": "0x000000204db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a10300000000000000005c93b3ce08ce22ba64fb26b8e75d695ac8c2bea0badd34200e698ccc73ca421b97121e58745104183ded0054", + "hash": "0x000000000000000000a024f601c50d9da8c3f09cc8feba477dd11370756fcda8", + "hash_le": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "height": 437475, + "prevhash": "0x000000000000000003a15741d88dcbdab6c39b17dddf4829b6b8b509313bb14d", + "prevhash_le": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "merkle_root": "0x1b42ca73cc8c690e2034ddbaa0bec2c85a695de7b826fb64ba22ce08ceb3935c", + "merkle_root_le": "0x5c93b3ce08ce22ba64fb26b8e75d695ac8c2bea0badd34200e698ccc73ca421b" + }, + { + "raw": "0x00000020a8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a00000000000000000004d7fd5d8c9984f2be03a80a75a4b6fe02dfda894181fcab0b6beb60698c7d6f0db121e587451041816e7fbbd", + "hash": "0x000000000000000003464978084011690021563526c9a5e2cbb17fbb76073adb", + "hash_le": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", + "height": 437476, + "prevhash": "0x000000000000000000a024f601c50d9da8c3f09cc8feba477dd11370756fcda8", + "prevhash_le": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "merkle_root": "0xf0d6c79806b6beb6b0ca1f1894a8fd2de06f4b5aa7803ae02b4f98c9d8d57f4d", + "merkle_root_le": "0x4d7fd5d8c9984f2be03a80a75a4b6fe02dfda894181fcab0b6beb60698c7d6f0" + }, + { + "raw": "0x00000020db3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000c8cbccf35d668296e8da72aed974983a8679e5077445b79d1e3e6c4d1bbff2c730161e58745104188674ac6f", + "hash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", + "hash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "height": 437477, + "prevhash": "0x000000000000000003464978084011690021563526c9a5e2cbb17fbb76073adb", + "prevhash_le": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", + "merkle_root": "0xc7f2bf1b4d6c3e1e9db7457407e579863a9874d9ae72dae89682665df3cccbc8", + "merkle_root_le": "0xc8cbccf35d668296e8da72aed974983a8679e5077445b79d1e3e6c4d1bbff2c7" + }, + { + "raw": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000f7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c073347161e58745104186cff9846", + "hash": "0x0000000000000000013f1f231297f03c154bf918650793a786b3468ab9c339d3", + "hash_le": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", + "height": 437478, + "prevhash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", + "prevhash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "merkle_root": "0x33074cebe67c59e730333ae386f8bd8f05fdda32c08fe143ac2b44e5e22ae2f7", + "merkle_root_le": "0xf7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c0733" + }, + { + "raw": "0x00000020d339c3b98a46b386a793076518f94b153cf09712231f3f01000000000000000018f078c9c73734d892fef90854381524f30017370d12dfa8319313c5729dbf85b8191e58745104187d43c005", + "hash": "0x000000000000000003a243f554646535630675ac1fb5a922a5bacc4df24d77a0", + "hash_le": "0xa0774df24dccbaa522a9b51fac75066335656454f543a2030000000000000000", + "height": 437479, + "prevhash": "0x0000000000000000013f1f231297f03c154bf918650793a786b3468ab9c339d3", + "prevhash_le": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", + "merkle_root": "0x85bf9d72c5139331a8df120d371700f32415385408f9fe92d83437c7c978f018", + "merkle_root_le": "0x18f078c9c73734d892fef90854381524f30017370d12dfa8319313c5729dbf85" + } + ], + "testCases": [{ + "ancestor": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "left": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "right": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "limit": 5, + "output": true + }, { + "ancestor": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "left": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", + "right": "0xf24d697743f1fce5857ee6f7d709ed60830ae4239da71d010000000000000000", + "limit": 5, + "output": true + }, { + "ancestor": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "left": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "right": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "limit": 5, + "output": true + }, { + "ancestor": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "left": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "right": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "limit": 5, + "output": false + }, { + "ancestor": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "left": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "right": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "limit": 1, + "output": false + }] + }, + "heaviestFromAncestor": { + "orphan": { + "raw": "0x0000802020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f0500000000000000000058c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53e7d3615c886f2e17ed205930", + "hash": "0x0000000000000000000f021d6ba1384b7db92589b2eee80d01dcc0d5afb30452", + "hash_le": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000", + "height": 562630, + "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x532bc27f15535eb6e606e34d16dedceb333513b212f56f3d0ce0e8f86bafc858", + "merkle_root_le": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" + }, + "badHeader": { + "raw": "0x00000020fe70e48339d6b17fbbf1340d245338f57336e97767cc240000000000000000005af53b865c27c6e9b5e5db4c3ea8e024f8329178a79ddb39f7727ea2fe6e6825d1349c5ba1192817e2d95159", + "hash": "0x0000000000000000000f021d6ba1384b7db92589b2eee80d01dcc0d5afb30452", + "hash_le": "0xbaaea6746f4c16ccb7cd961655b636d39b5fe1519b8f15000000000000000000", + "height": 562630, + "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x532bc27f15535eb6e606e34d16dedceb333513b212f56f3d0ce0e8f86bafc858", + "merkle_root_le": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" + }, + "genesis": { + "raw": "0x00000020db62962b5989325f30f357762ae456b2ec340432278e14000000000000000000d1dd4e30908c361dfeabfb1e560281c1a270bde3c8719dbda7c848005317594440bf615c886f2e17bd6b082d", + "hash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "hash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "height": 562621, + "prevhash": "0x000000000000000000148e27320434ecb256e42a7657f3305f3289592b9662db", + "prevhash_le": "0xdb62962b5989325f30f357762ae456b2ec340432278e14000000000000000000", + "merkle_root": "0x445917530048c8a7bd9d71c8e3bd70a2c18102561efbabfe1d368c90304eddd1", + "merkle_root_le": "0xd1dd4e30908c361dfeabfb1e560281c1a270bde3c8719dbda7c8480053175944" + }, + "headers": [ + { + "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325", + "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "height": 562622, + "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", + "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", + "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a" + }, + { + "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4", + "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "height": 562623, + "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", + "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", + "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c" + }, + { + "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758", + "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "height": 562624, + "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", + "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", + "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534" + }, + { + "raw": "0x00e0ff3f8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000e78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca8149dc3615c886f2e171c3fa2b8", + "hash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", + "hash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "height": 562625, + "prevhash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", + "prevhash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "merkle_root": "0x14a87c0e6d701c62e4cb4f3034f55bcbfd670657aa853e9b46ef9524c16582e7", + "merkle_root_le": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814" + }, + { + "raw": "0x00000020fee20039fe494f7408c090e03970ec9b132366066f380d0000000000000000006f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533dacec3615c886f2e170a1a921d", + "hash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", + "hash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "height": 562626, + "prevhash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", + "prevhash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "merkle_root": "0xda33c59bc6f3c950d8bc9be1779b0c03ddc67f7ab9304fb62cd456a1840b516f", + "merkle_root_le": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da" + }, + { + "raw": "0x00000020d2406bb15e4f917104d2b2d9320454b947fb08a723301d0000000000000000006aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20d1c5615c886f2e1767973d73", + "hash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", + "hash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "height": 562627, + "prevhash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", + "prevhash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "merkle_root": "0x20ead5553be95c71e87839492197ea48ccf87dbeb0b25ed4bc4e97bb0214a36a", + "merkle_root_le": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20" + }, + { + "raw": "0x00e0002096f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b0000000000000000002488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe953ce615c886f2e172e0885ce", + "hash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", + "hash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "height": 562628, + "prevhash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", + "prevhash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "merkle_root": "0xe9ab8cb51b64c924b61c52f8be389955c983ae2315ee042dc76cab3b3a338824", + "merkle_root_le": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9" + }, + { + "raw": "0x00000020b86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000e72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7fcdce615c886f2e17415cdb46", + "hash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "hash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "height": 562629, + "prevhash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", + "prevhash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "merkle_root": "0x7f8d7afb20451eb0dfd026ecc2f71e9f126fc536939fe3de6ed324d36f0a2ee7", + "merkle_root_le": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f" + }, + { + "raw": "0x0000002020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f050000000000000000009d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ecebd3615c886f2e179234c38e", + "hash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", + "hash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "height": 562630, + "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", + "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0xec8294c76e141d9244c6fec847fea8dbbb529933b279a2102a61da7f5179149d", + "merkle_root_le": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec" + }, + { + "raw": "0x0000002051214b0c42383a1ea7bf28f20062f81d7b72497cb1030a00000000000000000000af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab3314bd8615c886f2e17f152bc1f", + "hash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", + "hash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "height": 562631, + "prevhash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", + "prevhash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "merkle_root": "0x31b36a8983956a484b5ba867fa9cd7e70ae0b4c78dcbe6da1353eb564744af00", + "merkle_root_le": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331" + }, + { + "raw": "0x00e00020bf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000b2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d032db615c886f2e17155f22df", + "hash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", + "hash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "height": 562632, + "prevhash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", + "prevhash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "merkle_root": "0xd029d1fadb3f9e673ff5155d227918c860962dcc1999bb77d6e2d655b5fcc2b2", + "merkle_root_le": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0" + }, + { + "raw": "0x00000020a4c292016c1585e6f81986f7e216c79d28b15b1d513a100000000000000000004e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35c0de615c886f2e17032ddf4b", + "hash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", + "hash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "height": 562633, + "prevhash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", + "prevhash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "merkle_root": "0x351f74fe8fab94e161ce4f9d2f2da2cac1b7af93abe7054a1d37635f5cf7334e", + "merkle_root_le": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35" + }, + { + "raw": "0x00000020577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000ef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180cdde615c886f2e171f2b5f80", + "hash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", + "hash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "height": 562634, + "prevhash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", + "prevhash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "merkle_root": "0x80214eef007bb437affecc7279cd81fa215e40096f47622d90e19ab0520d20ef", + "merkle_root_le": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180" + }, + { + "raw": "0x00000020f5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000a91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589cb3e0615c886f2e17c624e58c", + "hash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", + "hash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "height": 562635, + "prevhash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", + "prevhash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "merkle_root": "0x9c58834eb6b069f57886efe06fe2af4be50cf82446160609e728867bfc3817a9", + "merkle_root_le": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c" + }, + { + "raw": "0x00e00020f98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000c29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2fae4615c886f2e178a505d9d", + "hash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", + "hash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "height": 562636, + "prevhash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", + "prevhash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "merkle_root": "0xc2c7bcd4be5c099df57632870ccf7cc345df60d46576197321ac90fef0149bc2", + "merkle_root_le": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2" + }, + { + "raw": "0x000000200ed18ffcb751e45471dddab23d34538869d3b2cdd484280000000000000000003505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d657ce5615c886f2e17d759c936", + "hash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", + "hash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "height": 562637, + "prevhash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", + "prevhash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "merkle_root": "0x650d7ffc89ce1160ed30f83a2c0bbe807f289987076e19f1916f861929700535", + "merkle_root_le": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65" + }, + { + "raw": "0x0000802030ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882f1e5615c886f2e1796e65983", + "hash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", + "hash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "height": 562638, + "prevhash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", + "prevhash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "merkle_root": "0x8258a466e5628d96ced147e5e18955923699b2daad38be299e42490880327a09", + "merkle_root_le": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882" + }, + { + "raw": "0x0000802051f32093b60f4484c041d383fb6bf35c44d428bde54d2d0000000000000000001888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee905e6615c886f2e177b59d28f", + "hash": "0x0000000000000000000fb8dba7f48eb65dbc24c6dc03d6f84823b5d59c7c4148", + "hash_le": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", + "height": 562639, + "prevhash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", + "prevhash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "merkle_root": "0xe9bedf6cd3fe3df1a7ba1d194d42a5caca6a3b323ed28270c2db2ff72d698818", + "merkle_root_le": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9" + } + ], + "testCases": [{ + "ancestor": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "currentBest": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "limit": 20, + "error": 103, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "limit": 20, + "error": 103, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "newBest": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "limit": 20, + "error": 103, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "limit": 20, + "error": 104, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "newBest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "limit": 20, + "error": 104, + "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "limit": 20, + "error": 0, + "output": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "newBest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "limit": 20, + "error": 0, + "output": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "newBest": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000", + "limit": 20, + "error": 0, + "output": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000" + }, { + "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "currentBest": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000", + "newBest": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "limit": 20, + "error": 0, + "output": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000" + }] + } + }, + "markNewHeaviest": { + "testCases": [{ + "ancestor": "0x9b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", + "currentBest": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", + "newBest": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", + "limit": 10, + "error": 403, + "output": "" + }, { + "ancestor": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", + "currentBest": "0x00000020d0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f200000000000000000005e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594af3fe1d58d2550418e90ec632", + "newBest": "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", + "limit": 10, + "error": 103, + "output": "" + }, { + "ancestor": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", + "currentBest": "0x0000002016d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f5b001e58d2550418183b1b41", + "newBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", + "limit": 20, + "error": 0, + "output": "extension" + }, { + "ancestor": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "currentBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", + "newBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000f7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c073347161e58745104186cff9846", + "limit": 10, + "error": 405, + "output": "" + }] + } } From 98a7ce1e81d5b63973e0e6454e1d7cb185707809 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Fri, 10 Jan 2020 15:09:17 -0700 Subject: [PATCH 051/148] Proof request (#25) * started request store * added MsgNewRequest * fixed set and get request, added checkRequests * added id for requests * updated setRequest * added Proof Request event * added query struct for get request by ID * made smaller prefix store for request ids * undid requestID store * fixed stuff * added errors to checkRequests * moved validation to ValidateBasic, updated other stuff * forgot msg handler * forgot query steps * added getRequest query to cli * typo, registered MsgNewRequest * small fix * added newrequest message to cli * delete validator code from keeper * changed json unmarshal to parseUint * added error to getRequest if request is not found * more stuff * deleted commented code, updated GetCmdNewRequest to use hex deserialization * added getNextID function * reverted getNextID * fixed lint errors --- golang/README.md | 2 +- golang/x/relay/alias.go | 2 + golang/x/relay/client/cli/query.go | 47 ++++++++- golang/x/relay/client/cli/tx.go | 68 +++++++----- golang/x/relay/client/rest/query.go | 41 +++++++- golang/x/relay/client/rest/rest.go | 2 + golang/x/relay/client/rest/tx.go | 79 +++++++------- golang/x/relay/keeper/chain_test.go | 1 + golang/x/relay/keeper/handler.go | 21 ++++ golang/x/relay/keeper/links_test.go | 6 +- golang/x/relay/keeper/querier.go | 32 +++++- golang/x/relay/keeper/querier_test.go | 8 +- golang/x/relay/keeper/requests.go | 144 ++++++++++++++++++++++++++ golang/x/relay/types/codec.go | 2 +- golang/x/relay/types/errors.go | 98 +++++++++++++++++- golang/x/relay/types/events.go | 22 +++- golang/x/relay/types/keys.go | 5 + golang/x/relay/types/msgs.go | 44 ++++++++ golang/x/relay/types/querier.go | 22 ++++ golang/x/relay/types/requests.go | 9 ++ 20 files changed, 573 insertions(+), 82 deletions(-) create mode 100644 golang/x/relay/keeper/requests.go create mode 100644 golang/x/relay/types/requests.go diff --git a/golang/README.md b/golang/README.md index 577b5cd7..d5a3f0b6 100644 --- a/golang/README.md +++ b/golang/README.md @@ -39,7 +39,7 @@ 1. Add new case block to `switch` in `NewHandler()` --- DO UP TO HERE FOR NOW --- 1. Add aliases in `x/relay/alias.go` - 1. Add alisa in `var` block + 1. Add alias in `var` block 1. Add alias in `type` block 1. Add to CLI 1. add to `x/relay/client/cli/tx.go` diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index 11643a1e..484b5194 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -25,6 +25,8 @@ var ( NewMsgIngestDifficultyChange = types.NewMsgIngestDifficultyChange // NewMsgMarkNewHeaviest is what is says on the tin NewMsgMarkNewHeaviest = types.NewMsgMarkNewHeaviest + // NewMsgGetRequest is what is says on the tin + NewMsgNewRequest = types.NewMsgNewRequest // RegisterCodec is what is says on the tin RegisterCodec = types.RegisterCodec // ModuleCdc is what is says on the tin diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index 070c699f..99b149f7 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -49,7 +49,7 @@ func GetCmdIsAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { var limit uint32 if len(args) == 3 { - lim, err := strconv.ParseUint(args[2], 0, 32) + lim, err := strconv.ParseUint(args[2], 10, 32) if err != nil { fmt.Print(err.Error()) return nil @@ -157,7 +157,7 @@ func GetCmdFindAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { } var offset uint32 - off, err := strconv.ParseUint(args[1], 0, 32) + off, err := strconv.ParseUint(args[1], 10, 32) if err != nil { fmt.Print(err.Error()) return nil @@ -219,7 +219,7 @@ func GetCmdIsMostRecentCommonAncestor(queryRoute string, cdc *codec.Codec) *cobr var limit uint32 if len(args) == 4 { - lim, err := strconv.ParseUint(args[3], 0, 32) + lim, err := strconv.ParseUint(args[3], 10, 32) if err != nil { fmt.Print(err.Error()) return nil @@ -284,7 +284,7 @@ func GetCmdHeaviestFromAncestor(queryRoute string, cdc *codec.Codec) *cobra.Comm var limit uint32 if len(args) == 4 { - lim, err := strconv.ParseUint(args[3], 0, 32) + lim, err := strconv.ParseUint(args[3], 10, 32) if err != nil { fmt.Print(err.Error()) return nil @@ -318,3 +318,42 @@ func GetCmdHeaviestFromAncestor(queryRoute string, cdc *codec.Codec) *cobra.Comm }, } } + +// GetCmdGetRequest returns the CLI command struct for getRequest +func GetCmdGetRequest(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "getrequest ", + Example: "getrequest 12", + Long: "Get a proof request using the associated ID", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + id, err := strconv.ParseUint(args[0], 10, 64) + if err != nil { + return err + } + + params := types.QueryParamsGetRequest{ + ID: id, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/getrequest", queryData) + + if err != nil { + fmt.Printf("could not find request associated with id: %d... \n", id) + return nil + } + + var out types.QueryResGetRequest + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 5e8eae67..9271ee15 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -2,6 +2,8 @@ package cli import ( "encoding/json" + "strconv" + "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" @@ -11,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -35,7 +38,7 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "ingestheaders ", - Short: "ingest a set of headers", + Short: "Ingest a set of headers", Long: "Ingest a set of headers. The headers must be in order, and the header immediately before the first must already be known to the relay", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -44,7 +47,6 @@ func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) // note: unmarshalling json here is undesirable, but necessary - // for MarkNewHeaviest we want to accept text instead of json var headers []types.BitcoinHeader jsonErr := json.Unmarshal([]byte(args[0]), &headers) if jsonErr != nil { @@ -66,25 +68,43 @@ func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { } } -// // GetCmdSetLink is the CLI command for sending a SetLink transaction -// func GetCmdSetLink(cdc *codec.Codec) *cobra.Command { -// return &cobra.Command{ -// Use: "setlink [hex header] [signer address]", -// Short: "Set a link", -// Args: cobra.ExactArgs(2), -// RunE: func(cmd *cobra.Command, args []string) error { -// cliCtx := context.NewCLIContext().WithCodec(cdc) -// -// txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) -// -// addr := sdk.AccAddress(args[1]) -// msg := types.NewMsgSetLink(addr, args[0], cliCtx.GetFromAddress()) -// err := msg.ValidateBasic() -// if err != nil { -// return err -// } -// -// return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) -// }, -// } -// } +// GetCmdNewRequest stores a new proof request +func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "newrequest ", + Short: "Stores a new proof request", + Long: "Stores a new proof request", + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + spends := btcspv.DecodeIfHex(args[0]) + pays := btcspv.DecodeIfHex(args[1]) + paysValue, valueErr := strconv.ParseUint(args[2], 10, 64) + if valueErr != nil { + return valueErr + } + numConfs, confsErr := strconv.ParseUint(args[3], 10, 8) + if confsErr != nil { + return confsErr + } + + msg := types.NewMsgNewRequest( + cliCtx.GetFromAddress(), + spends, + pays, + paysValue, + uint8(numConfs), + ) + err := msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + + }, + } +} diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index ae9ec14a..64a1d001 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -32,7 +32,7 @@ func isAncestorHandler(cliCtx context.CLIContext, storeName string) http.Handler var limit uint32 if val, ok := vars["limit"]; ok { - lim, err := strconv.ParseUint(val, 0, 32) + lim, err := strconv.ParseUint(val, 10, 32) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) return @@ -104,7 +104,7 @@ func findAncestorHandler(cliCtx context.CLIContext, storeName string) http.Handl return } - off, err := strconv.ParseUint(vars["offset"], 0, 32) + off, err := strconv.ParseUint(vars["offset"], 10, 32) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) return @@ -162,7 +162,7 @@ func isMostRecentCommonAncestorHandler(cliCtx context.CLIContext, storeName stri var limit uint32 if val, ok := vars["limit"]; ok { - lim, err := strconv.ParseUint(val, 0, 32) + lim, err := strconv.ParseUint(val, 10, 32) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) return @@ -223,7 +223,7 @@ func heaviestFromAncestorHandler(cliCtx context.CLIContext, storeName string) ht var limit uint32 if val, ok := vars["limit"]; ok { - lim, err := strconv.ParseUint(val, 0, 32) + lim, err := strconv.ParseUint(val, 10, 32) if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) return @@ -256,3 +256,36 @@ func heaviestFromAncestorHandler(cliCtx context.CLIContext, storeName string) ht rest.PostProcessResponse(w, cliCtx, res) } } + +// handler function for getRequest queries. parses arguments from url string, and passes them through +// as a QueryParamsGetReqiest struct +func getRequestHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + + id, err := strconv.ParseUint(vars["id"], 10, 64) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + params := types.QueryParamsGetRequest{ + ID: id, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, _, err := cliCtx.QueryWithData("custom/relay/getRequest", queryData) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index 37a75a1d..333796bd 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -14,6 +14,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) // add new tx msg routes here s.HandleFunc("/ingestheaderchain", ingestHeaderChainHandler(cliCtx)).Methods("POST") + s.HandleFunc("/newrequest", newRequestHandler(cliCtx)).Methods("POST") // add new query routes below // {} denotes variable parts of the url route @@ -27,4 +28,5 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) s.HandleFunc("/ismostrecentcommonancestor/{ancestor}/{left}/{right}/{limit}", isMostRecentCommonAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/{limit}", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/getrequest/{id}", getRequestHandler(cliCtx, storeName)).Methods("GET") } diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 52076961..9283b058 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -11,42 +11,7 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) -// type setLinkReq struct { -// BaseReq rest.BaseReq `json:"base_req"` -// Header string `json:"header"` -// Sender sdk.AccAddress `json:"sender"` -// } -// -// func setLinkHandler(cliCtx context.CLIContext) http.HandlerFunc { -// return func(w http.ResponseWriter, r *http.Request) { -// var req setLinkReq -// -// if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { -// rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") -// return -// } -// -// baseReq := req.BaseReq.Sanitize() -// if !baseReq.ValidateBasic(w) { -// return -// } -// -// addr := sdk.AccAddress(req.Sender) -// -// // create the message -// msg := types.NewMsgSetLink(req.Sender, req.Header, addr) -// err := msg.ValidateBasic() -// -// if err != nil { -// rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) -// return -// } -// -// utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) -// } -// } - -type ingestHeaderChainReq struct { +type IngestHeaderChainReq struct { BaseReq rest.BaseReq `json:"base_req"` Headers []types.BitcoinHeader `json:"headers"` Sender string `json:"sender"` @@ -54,7 +19,7 @@ type ingestHeaderChainReq struct { func ingestHeaderChainHandler(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req ingestHeaderChainReq + var req IngestHeaderChainReq if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") @@ -82,3 +47,43 @@ func ingestHeaderChainHandler(cliCtx context.CLIContext) http.HandlerFunc { utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) } } + +type NewRequestReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Spends []byte `json:"spends"` + Pays []byte `json:"pays"` + PaysValue uint64 `json:"paysValue"` + NumConfs uint8 `json:"numConfs"` + Sender string `json:"sender"` +} + +func newRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req NewRequestReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgNewRequest(addr, req.Spends, req.Pays, req.PaysValue, req.NumConfs) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} diff --git a/golang/x/relay/keeper/chain_test.go b/golang/x/relay/keeper/chain_test.go index 4c7b6626..44dc1fa7 100644 --- a/golang/x/relay/keeper/chain_test.go +++ b/golang/x/relay/keeper/chain_test.go @@ -146,6 +146,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { tc[i].NewBest, tc[i].Limit, ) + s.SDKNil(err) events := s.Context.EventManager().Events() e := events[0] s.Equal(e.Type, tc[i].Output) diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index d8c4329e..6a573a54 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -17,6 +17,8 @@ func NewHandler(keeper Keeper) sdk.Handler { return handleMsgIngestDifficultyChange(ctx, keeper, msg) case types.MsgMarkNewHeaviest: return handleMsgMarkNewHeaviest(ctx, keeper, msg) + case types.MsgNewRequest: + return handleMsgNewRequest(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized relay Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() @@ -53,3 +55,22 @@ func handleMsgMarkNewHeaviest(ctx sdk.Context, keeper Keeper, msg types.MsgMarkN Events: ctx.EventManager().Events(), } } + +func handleMsgNewRequest(ctx sdk.Context, keeper Keeper, msg types.MsgNewRequest) sdk.Result { + // Validate message + err := msg.ValidateBasic() + if err != nil { + return err.Result() + } + + // TODO: Add more complex permissioning + // Set request + err = keeper.setRequest(ctx, msg.Spends, msg.Pays, msg.PaysValue, msg.NumConfs) + if err != nil { + return err.Result() + } + + return sdk.Result{ + Events: ctx.EventManager().Events(), + } +} diff --git a/golang/x/relay/keeper/links_test.go b/golang/x/relay/keeper/links_test.go index 0afceb83..63b6bc6a 100644 --- a/golang/x/relay/keeper/links_test.go +++ b/golang/x/relay/keeper/links_test.go @@ -26,7 +26,8 @@ func (s *KeeperSuite) TestFindAncestor() { s.Equal(err.Code(), types.CodeType(tc[0].Error)) s.Keeper.ingestHeader(s.Context, anchor) - s.Keeper.IngestHeaderChain(s.Context, headers) + err = s.Keeper.IngestHeaderChain(s.Context, headers) + s.SDKNil(err) for i := 1; i < len(tc); i++ { if tc[i].Error == 0 { @@ -48,7 +49,8 @@ func (s *KeeperSuite) TestIsAncestor() { tc := s.Fixtures.LinkTestCases.IsAncestor.TestCases s.Keeper.ingestHeader(s.Context, anchor) - s.Keeper.IngestHeaderChain(s.Context, headers) + err := s.Keeper.IngestHeaderChain(s.Context, headers) + s.SDKNil(err) for i := range tc { isAncestor := s.Keeper.IsAncestor(s.Context, tc[i].Digest, tc[i].Ancestor, tc[i].Limit) diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index f0c1fd5a..3c28acbc 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -15,7 +15,7 @@ func decodeUint32FromPath(path []string, idx int, defaultLimit uint32) (uint32, return defaultLimit, nil } // parse int from path[idx], return error if necessary - num, convErr := strconv.ParseUint(path[idx], 0, 32) + num, convErr := strconv.ParseUint(path[idx], 10, 32) if convErr != nil { return defaultLimit, types.ErrExternal(types.DefaultCodespace, convErr) } @@ -38,6 +38,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryHeaviestFromAncestor(ctx, req, keeper) case types.QueryIsMostRecentCommonAncestor: return queryIsMostRecentCommonAncestor(ctx, req, keeper) + case types.QueryGetRequest: + return queryGetRequest(ctx, req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } @@ -204,3 +206,31 @@ func queryIsMostRecentCommonAncestor(ctx sdk.Context, req abci.RequestQuery, kee } return res, nil } + +func queryGetRequest(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsGetRequest + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) + } + + // This calls the keeper with the parsed arguments, and gets an answer + result, resErr := keeper.getRequest(ctx, params.ID) + if resErr != nil { + return []byte{}, resErr + } + + // Now we format the answer as a response + response := types.QueryResGetRequest{ + Params: params, + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index e58df838..eb8b6197 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -81,7 +81,8 @@ func (s *KeeperSuite) TestQueryIsAncestor() { querier := NewQuerier(s.Keeper) s.Keeper.ingestHeader(s.Context, anchor) - s.Keeper.IngestHeaderChain(s.Context, headers) + err := s.Keeper.IngestHeaderChain(s.Context, headers) + s.SDKNil(err) params := types.QueryParamsIsAncestor{ DigestLE: headers[4].HashLE, @@ -223,7 +224,8 @@ func (s *KeeperSuite) TestQueryFindAncestor() { // initialize data s.Keeper.ingestHeader(s.Context, anchor) - s.Keeper.IngestHeaderChain(s.Context, headers) + ingestErr := s.Keeper.IngestHeaderChain(s.Context, headers) + s.SDKNil(ingestErr) // test that it retrieves the correct ancestor res, err := querier(s.Context, path, req) @@ -302,7 +304,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { Data: marshalledParams, } - res, err = querier(s.Context, path, req) + _, err = querier(s.Context, path, req) s.Equal(err.Code(), sdk.CodeType(103)) // Test that default limit is used if limit is set to zero diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go new file mode 100644 index 00000000..1e22d582 --- /dev/null +++ b/golang/x/relay/keeper/requests.go @@ -0,0 +1,144 @@ +package keeper + +import ( + "encoding/binary" + "encoding/json" + + btcspv "github.com/summa-tx/bitcoin-spv/golang/btcspv" + "github.com/summa-tx/relays/golang/x/relay/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) emitProofRequest(ctx sdk.Context, pays, spends []byte, paysValue, id uint64) { + ctx.EventManager().EmitEvent(types.NewProofRequestEvent(pays, spends, paysValue, id)) +} + +func (k Keeper) getRequestStore(ctx sdk.Context) sdk.KVStore { + return k.getPrefixStore(ctx, types.RequestStorePrefix) +} + +func (k Keeper) hasRequest(ctx sdk.Context, id uint64) bool { + // convert id to bytes + idBytes := make([]byte, 8) + binary.BigEndian.PutUint64(idBytes, id) + + store := k.getRequestStore(ctx) + return store.Has(idBytes) +} + +func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValue uint64, numConfs uint8) sdk.Error { + store := k.getRequestStore(ctx) + + spendsDigest := btcspv.Hash256(spends) + paysDigest := btcspv.Hash256(pays) + + request := types.ProofRequest{ + Spends: spendsDigest, + Pays: paysDigest, + PaysValue: paysValue, + ActiveState: true, + NumConfs: numConfs, + } + + // When a new request comes in, get the id and use it to store request + id := k.getNextID(ctx) + + buf, err := json.Marshal(request) + if err != nil { + return types.ErrMarshalJSON(types.DefaultCodespace) + } + store.Set(id, buf) + + // Increment the ID + k.incrementID(ctx) + + // Emit Proof Request event + numID := binary.BigEndian.Uint64(id) + k.emitProofRequest(ctx, pays, spends, request.PaysValue, numID) + return nil +} + +func (k Keeper) getRequest(ctx sdk.Context, id uint64) (types.ProofRequest, sdk.Error) { + store := k.getRequestStore(ctx) + + // convert id to bytes + idBytes := make([]byte, 8) + binary.BigEndian.PutUint64(idBytes, id) + + hasRequest := k.hasRequest(ctx, id) + if !hasRequest { + return types.ProofRequest{}, types.ErrUnknownRequest(types.DefaultCodespace) + } + buf := store.Get(idBytes) + var request types.ProofRequest + jsonErr := json.Unmarshal(buf, &request) + if jsonErr != nil { + return types.ProofRequest{}, types.ErrExternal(types.DefaultCodespace, jsonErr) + } + return request, nil +} + +func (k Keeper) incrementID(ctx sdk.Context) { + store := k.getRequestStore(ctx) + // get id + id := k.getNextID(ctx) + // convert id to uint64 and add 1 + newID := binary.BigEndian.Uint64(id) + 1 + // convert back to bytes and store + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, newID) + store.Set([]byte(types.RequestID), b) +} + +// getNextID retrieves the ID. The ID is incremented after storing a request, +// so this returns the next ID to be used. +func (k Keeper) getNextID(ctx sdk.Context) []byte { + store := k.getRequestStore(ctx) + id := []byte(types.RequestID) + if !store.Has(id) { + store.Set(id, []byte{0}) + } + return store.Get(id) +} + +func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID uint64) (bool, sdk.Error) { + if !btcspv.ValidateVin(vin) { + return false, types.ErrInvalidVin(types.DefaultCodespace) + } + if !btcspv.ValidateVout(vout) { + return false, types.ErrInvalidVout(types.DefaultCodespace) + } + + req, reqErr := k.getRequest(ctx, requestID) + if reqErr != nil { + return false, reqErr + } + if !req.ActiveState { + return false, types.ErrClosedRequest(types.DefaultCodespace) + } + + hasPays := req.Pays != types.Hash256Digest{} + if hasPays { + // We can ignore this error because we know that ValidateVout passed + out, _ := btcspv.ExtractOutputAtIndex(vout, outputIndex) + outDigest := btcspv.Hash256(out[8:]) + if outDigest != req.Pays { + return false, types.ErrRequestPays(types.DefaultCodespace) + } + paysValue := req.PaysValue + if paysValue != 0 || uint64(btcspv.ExtractValue(out)) < paysValue { + return false, types.ErrRequestValue(types.DefaultCodespace) + } + } + + hasSpends := req.Spends != types.Hash256Digest{} + if hasSpends { + in := btcspv.ExtractInputAtIndex(vin, inputIndex) + inDigest := btcspv.Hash256(in) + if !hasSpends || inDigest != req.Spends { + return false, types.ErrRequestSpends(types.DefaultCodespace) + } + } + return true, nil +} diff --git a/golang/x/relay/types/codec.go b/golang/x/relay/types/codec.go index 9767cee9..25fe539f 100644 --- a/golang/x/relay/types/codec.go +++ b/golang/x/relay/types/codec.go @@ -16,5 +16,5 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgIngestHeaderChain{}, "relay/IngestHeaderChain", nil) cdc.RegisterConcrete(MsgIngestDifficultyChange{}, "relay/IngestDifficultyChange", nil) cdc.RegisterConcrete(MsgMarkNewHeaviest{}, "relay/MarkNewHeaviest", nil) - + cdc.RegisterConcrete(MsgNewRequest{}, "relay/NewRequest", nil) } diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index fcea3a98..71363e76 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -123,9 +123,56 @@ const ( // MarshalJSONMessage is the corresponding message MarshalJSONMessage = "Could not marshal result to JSON" - // 600-block External - - ExternalError sdk.CodeType = 601 + // 600-block Proof Requests + + // UnknownRequest means the request was not found + UnknownRequest sdk.CodeType = 601 + // UnknownRequestMessage is the corresponding message + UnknownRequestMessage = "Request not found" + + // SpendsLength means the spend value is not 36 bytes + SpendsLength sdk.CodeType = 602 + // SpendsLengthMessage is the corresponding message + SpendsLengthMessage = "Spends value is not 36 bytes" + + // PaysLength means the pays value is greater than 50 bytes + PaysLength sdk.CodeType = 603 + // PaysLengthMessage is the corresponding message + PaysLengthMessage = "Pays value is greater than 50 bytes" + + // InvalidVin means the vin is not valid + InvalidVin sdk.CodeType = 604 + // InvalidVinMessage is the corresponding message + InvalidVinMessage = "Vin is not valid" + + // InvalidVout means the vout is not valid + InvalidVout sdk.CodeType = 605 + // InvalidVoutMessage is the corresponding message + InvalidVoutMessage = "Vout is not valid" + + // ClosedRequest means the request is not active + ClosedRequest sdk.CodeType = 606 + // ClosedRequestMessage is the corresponding message + ClosedRequestMessage = "Request is not active" + + // RequestPays means the output does not match the pays request + RequestPays sdk.CodeType = 607 + // RequestPaysMessage is the corresponding message + RequestPaysMessage = "Does not match pays request" + + // RequestValue means the pays value and value of the output does not match + RequestValue sdk.CodeType = 608 + // RequestValueMessage is the corresponding message + RequestValueMessage = "Does not match value request" + + // RequestSpends means the request spends does not match the input + RequestSpends sdk.CodeType = 609 + // RequestSpendsMessage is the corresponding message + RequestSpendsMessage = "Does not match spends request" + + // 700-block External + + ExternalError sdk.CodeType = 701 ) // ErrUnknownError throws an error @@ -233,6 +280,51 @@ func FromBTCSPVError(codespace sdk.CodespaceType, err error) sdk.Error { return sdk.NewError(codespace, BitcoinSPV, err.Error()) } +// ErrUnknownRequest throws an error +func ErrUnknownRequest(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, UnknownRequest, UnknownRequestMessage) +} + +// ErrSpendsLength throws an error +func ErrSpendsLength(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, SpendsLength, SpendsLengthMessage) +} + +// ErrPaysLength throws an error +func ErrPaysLength(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, PaysLength, PaysLengthMessage) +} + +// ErrInvalidVin throws an error +func ErrInvalidVin(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, InvalidVin, InvalidVinMessage) +} + +// ErrInvalidVout throws an error +func ErrInvalidVout(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, InvalidVout, InvalidVoutMessage) +} + +// ErrClosedRequest throws an error +func ErrClosedRequest(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, ClosedRequest, ClosedRequestMessage) +} + +// ErrRequestPays throws an error +func ErrRequestPays(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, RequestPays, RequestPaysMessage) +} + +// ErrRequestValue throws an error +func ErrRequestValue(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, RequestValue, RequestValueMessage) +} + +// ErrRequestSpends throws an error +func ErrRequestSpends(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, RequestSpends, RequestSpendsMessage) +} + // ErrExternal converts any external error into an sdk error func ErrExternal(codespace sdk.CodespaceType, err error) sdk.Error { return sdk.NewError(codespace, ExternalError, err.Error()) diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index bdf3670c..8241e6ba 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -2,14 +2,16 @@ package types import ( "encoding/hex" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" ) // Relay module event types const ( - EventTypeExtension = "extension" - EventTypeReorg = "reorg" + EventTypeExtension = "extension" + EventTypeReorg = "reorg" + EventTypeProofRequest = "proof_request" AttributeKeyFirstBlock = "first_block" AttributeKeyLastBlock = "last_block" @@ -17,6 +19,11 @@ const ( AttributeKeyPreviousBest = "previous_best" AttributeKeyNewBest = "new_best" AttributeKeyLatestCommon = "latest_common_ancestor" + + AttributeKeyRequestID = "request_id" + AttributeKeyPays = "pays" + AttributeKeySpends = "spends" + AttributeKeyPaysValue = "value" ) // NewReorgEvent instantiates a reorg event @@ -37,3 +44,14 @@ func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { sdk.NewAttribute(AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.HashLE[:])), ) } + +// NewExtensionEvent instantiates a proof request event +func NewProofRequestEvent(pays, spends []byte, paysValue, id uint64) sdk.Event { + return sdk.NewEvent( + EventTypeProofRequest, + sdk.NewAttribute(AttributeKeyRequestID, fmt.Sprintf("%d", id)), + sdk.NewAttribute(AttributeKeyPays, "0x"+hex.EncodeToString(pays[:])), + sdk.NewAttribute(AttributeKeySpends, "0x"+hex.EncodeToString(spends[:])), + sdk.NewAttribute(AttributeKeyPaysValue, fmt.Sprintf("%d", paysValue)), + ) +} diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index be796576..b57d81d9 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -13,6 +13,9 @@ const ( // HeaderStorePrefix to be used when accessing headers HeaderStorePrefix = ModuleName + "-headers-" + // RequestStorePrefix to be used when making requests + RequestStorePrefix = ModuleName + "-requests-" + // ChainStorePrefix to be used when accessing chain metadata ChainStorePrefix = ModuleName + "-chain-" @@ -24,4 +27,6 @@ const ( // LastReorgLCAStorage is the storage key for the last reorg LCA LastReorgLCAStorage = "LastReorgLCA" + + RequestID = "id" ) diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 9bdd637d..fb11c89b 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -123,3 +123,47 @@ func (msg MsgMarkNewHeaviest) GetSignBytes() []byte { } func (msg MsgMarkNewHeaviest) Route() string { return RouterKey } + +/***** NewRequest *****/ + +// MsgNewRequest defines a NewRequest message +type MsgNewRequest struct { + Signer sdk.AccAddress `json:"signer"` + Spends []byte `json:"spends"` + Pays []byte `json:"pays"` + PaysValue uint64 `json:"paysValue"` + NumConfs uint8 `json:"numConfs"` +} + +func NewMsgNewRequest(address sdk.AccAddress, spends, pays []byte, paysValue uint64, numConfs uint8) MsgNewRequest { + return MsgNewRequest{ + address, + spends, + pays, + paysValue, + numConfs, + } +} + +func (msg MsgNewRequest) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} + +func (msg MsgNewRequest) Type() string { return "new_request" } + +func (msg MsgNewRequest) ValidateBasic() sdk.Error { + // TODO: validate output types + if len(msg.Spends) != 36 { + return ErrSpendsLength(DefaultCodespace) + } + if len(msg.Pays) > 50 { + return ErrPaysLength(DefaultCodespace) + } + return nil +} + +func (msg MsgNewRequest) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgNewRequest) Route() string { return RouterKey } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index a56dbbe0..0690e513 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -26,6 +26,9 @@ const ( // QueryIsMostRecentCommonAncestor is a query string tag for IsMostRecentCommonAncestor QueryIsMostRecentCommonAncestor = "ismostrecentcommonancestor" + + // QueryGetRequest is a query string tag for getRequest + QueryGetRequest = "getrequest" ) // QueryParamsIsAncestor represents the parameters for an IsAncestor query @@ -140,3 +143,22 @@ func (r QueryResIsMostRecentCommonAncestor) String() string { "Ancestor: %s, Left: %s, Right: %s, Limit: %d, Result: %t", anc, left, right, r.Params.Limit, r.Res) } + +type QueryParamsGetRequest struct { + ID uint64 `json:"id"` +} + +// QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor +type QueryResGetRequest struct { + Params QueryParamsGetRequest `json:"params"` + Res ProofRequest `json:"result"` +} + +// String formats a QueryResIsMostRecentCommonAncestor struct +func (r QueryResGetRequest) String() string { + spends := "0x" + hex.EncodeToString(r.Res.Spends[:]) + pays := "0x" + hex.EncodeToString(r.Res.Pays[:]) + return fmt.Sprintf( + "ID: %d, Spends: %s, Pays: %s, Value: %d, Active: %t, Confirmations: %d", + r.Params.ID, spends, pays, r.Res.PaysValue, r.Res.ActiveState, r.Res.NumConfs) +} diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go new file mode 100644 index 00000000..979e1bb8 --- /dev/null +++ b/golang/x/relay/types/requests.go @@ -0,0 +1,9 @@ +package types + +type ProofRequest struct { + Spends Hash256Digest `json:"spends"` + Pays Hash256Digest `json:"pays"` + PaysValue uint64 `json:"paysValue"` + ActiveState bool `json:"activeState"` + NumConfs uint8 `json:"numConfs"` +} From 7271e6f009fc691fefd610071fe468c0def06e77 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Wed, 15 Jan 2020 13:53:51 -0700 Subject: [PATCH 052/148] Add messages to CLI (#26) * finished adding methods to cli * changed to hex deserialization * README tables --- golang/README.md | 19 ++++++ golang/x/relay/client/cli/tx.go | 95 +++++++++++++++++++++++++++++- golang/x/relay/client/rest/rest.go | 2 + golang/x/relay/client/rest/tx.go | 78 ++++++++++++++++++++++++ golang/x/relay/keeper/headers.go | 1 - 5 files changed, 193 insertions(+), 2 deletions(-) diff --git a/golang/README.md b/golang/README.md index d5a3f0b6..8b15de3e 100644 --- a/golang/README.md +++ b/golang/README.md @@ -46,3 +46,22 @@ 1. Add to REST 1. add to `x/relay/client/rest/tx.go` 1. add POST route to `x/relay/client/rest/rest.go` + +## Messages added to CLI +| Message | Status | +| --- | --- | +| IngestHeaderChain | Completed | +| IngestDifficultyChange | Completed | +| MarkNewHeaviest | Completed | +| NewRequest | Completed | + +## Queries added to CLI +| Query | Status | +| --- | --- | +| IsAncestor | Completed | +| GetRelayGenesis | Completed | +| GetLastReorgLCA | Completed | +| FindAncestor | Completed | +| IsMostRecentCommonAncestor | Completed | +| HeaviestFromAncestor | Completed | +| GetRequest | Completed | diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 9271ee15..b6cfc543 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -13,8 +13,9 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" - "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" + + btcspv "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // GetTxCmd sets up transaction CLI commands @@ -68,6 +69,46 @@ func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { } } +// GetCmdIngestDifficultyChange creates a CLI command to ingest a difficulty change +func GetCmdIngestDifficultyChange(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "ingestdiffchange ", + Short: "Ingest a difficulty change.", + Long: "Ingest a difficulty change. Prev Epoch Start is a hex digest.", + Args: cobra.ExactArgs(2), + + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + prevEpochStart, err := btcspv.NewHash256Digest(btcspv.DecodeIfHex(args[0])) + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + var headers []types.BitcoinHeader + jsonErr := json.Unmarshal([]byte(args[1]), &headers) + if jsonErr != nil { + return jsonErr + } + + msg := types.NewMsgIngestDifficultyChange( + cliCtx.GetFromAddress(), + prevEpochStart, + headers, + ) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + + }, + } +} + // GetCmdNewRequest stores a new proof request func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ @@ -108,3 +149,55 @@ func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { }, } } + +// GetCmdMarkNewHeaviest creates a CLI command to update best known digest and LCA +func GetCmdMarkNewHeaviest(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "marknewheaviest [limit]", + Short: "Updates best known digest and LCA", + Long: "Updates best known digest and LCA.\nAncestor, current best, and new best are hex.\nLimit is an integer.", + Args: cobra.RangeArgs(3, 4), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + // TODO: Set default limit if limit is not provided? + + ancestor, err := btcspv.NewHash256Digest(btcspv.DecodeIfHex(args[0])) + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + currentBest, curBestErr := btcspv.NewRawHeader(btcspv.DecodeIfHex(args[1])) + if curBestErr != nil { + return types.FromBTCSPVError(types.DefaultCodespace, curBestErr) + } + + newBest, newBestErr := btcspv.NewRawHeader(btcspv.DecodeIfHex(args[2])) + if newBestErr != nil { + return types.FromBTCSPVError(types.DefaultCodespace, newBestErr) + } + + limit, err := strconv.ParseUint(args[3], 10, 32) + if err != nil { + return err + } + + msg := types.NewMsgMarkNewHeaviest( + cliCtx.GetFromAddress(), + ancestor, + currentBest, + newBest, + uint32(limit), + ) + err = msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + + }, + } +} diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index 333796bd..e3603cf0 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -14,6 +14,8 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) // add new tx msg routes here s.HandleFunc("/ingestheaderchain", ingestHeaderChainHandler(cliCtx)).Methods("POST") + s.HandleFunc("/ingestdiffchange", ingestDifficultyChangeHandler(cliCtx)).Methods("POST") + s.HandleFunc("/marknewheaviest", markNewHeaviestHandler(cliCtx)).Methods("POST") s.HandleFunc("/newrequest", newRequestHandler(cliCtx)).Methods("POST") // add new query routes below diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 9283b058..150cdacf 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -48,6 +48,84 @@ func ingestHeaderChainHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +type IngestDifficultyChangeReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Start types.Hash256Digest `json:"prevEpochStart"` + Headers []types.BitcoinHeader `json:"headers"` + Sender string `json:"sender"` +} + +func ingestDifficultyChangeHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req IngestDifficultyChangeReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgIngestDifficultyChange(addr, req.Start, req.Headers) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} + +type MarkNewHeaviestReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Ancestor types.Hash256Digest `json:"ancestor"` + CurrentBest types.RawHeader `json:"currentBest"` + NewBest types.RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` + Sender string `json:"sender"` +} + +func markNewHeaviestHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req MarkNewHeaviestReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := types.NewMsgMarkNewHeaviest(addr, req.Ancestor, req.CurrentBest, req.NewBest, req.Limit) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} + type NewRequestReq struct { BaseReq rest.BaseReq `json:"base_req"` Spends []byte `json:"spends"` diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index bb1285f1..3b652b69 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -78,7 +78,6 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead _, err := header.Validate() if err != nil { return types.FromBTCSPVError(types.DefaultCodespace, err) - // return types.FromBTCSPVError(types.DefaultCodespace, errors.New(fmt.Sprintf("%d err: %d", i, err))) } // ensure height changes as expected From df01e4580f18fd3f570ef25a0933d71207be6f7b Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 15 Jan 2020 14:37:35 -0800 Subject: [PATCH 053/148] chore: update progress tracking in README --- golang/README.md | 58 +++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/golang/README.md b/golang/README.md index 8b15de3e..0153ce31 100644 --- a/golang/README.md +++ b/golang/README.md @@ -1,11 +1,38 @@ ## TODOs: -1. Migrate solidity logic -1. Add iterator for links -1. Add reasonable genesis state -1. Add state export -1. Pass in testnet or mainnet status - 1. Maybe genesis state? that's not perfect tho +- [X] Milestone 1 +- [ ] Milestone 2 +- - [X] Expose best-known digest +- - [X] Expose LCA of reorg +- - [X] Follow API of existing Solidity Relay +- - [ ] Validate SPV Proofs +- [ ] Milestone 3 +- - [ ] Provide hooks to execute tasks + dispatch messages +- - [ ] Add a basic web dashboard with Relay health + +## Messages added to CLI + +| Message | Status | Description | +| ------- | ------ | ----------- | +| IngestHeaderChain | Completed | Add a chain of headers to the relay | +| IngestDifficultyChange | Completed | Add a chain of headers to the relay with a difficulty change| +| MarkNewHeaviest | Completed | Mark a new best-known chain tip | +| NewRequest | Completed | Register a new SPV Proof request| +| ProvideProof | TODO | Provide a proof that satisfies 1 or more requests | + +## Queries added to CLI + +| Query | Status | Description | +| ----- | ------ | ----------- | +| IsAncestor | Completed | Deteremine if a block is an ancestor of another | +| GetRelayGenesis | Completed | Get the trusted root of the relay | +| GetLastReorgLCA | Completed | Get the LCA of the latest reorg | +| FindAncestor | Completed | Find the nth ancestor of a block| +| IsMostRecentCommonAncestor | Completed | Determine if a block is the LCA of two headers| +| HeaviestFromAncestor | Completed | Check which of two descendents is heaviest from the LCA | +| GetRequest | Completed | Get details of an SPV Proof Request| +| CheckProof | TODO | Check the syntactic validity of an SPV Proof | +| CheckRequests | TODO | Perform CheckProof and check the SPV Proof against a set of Requests | ## How to add a view function 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` @@ -46,22 +73,3 @@ 1. Add to REST 1. add to `x/relay/client/rest/tx.go` 1. add POST route to `x/relay/client/rest/rest.go` - -## Messages added to CLI -| Message | Status | -| --- | --- | -| IngestHeaderChain | Completed | -| IngestDifficultyChange | Completed | -| MarkNewHeaviest | Completed | -| NewRequest | Completed | - -## Queries added to CLI -| Query | Status | -| --- | --- | -| IsAncestor | Completed | -| GetRelayGenesis | Completed | -| GetLastReorgLCA | Completed | -| FindAncestor | Completed | -| IsMostRecentCommonAncestor | Completed | -| HeaviestFromAncestor | Completed | -| GetRequest | Completed | From c5411dcd5bdf07344a7d0b19269a252eeb11b488 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Wed, 22 Jan 2020 13:24:03 -0700 Subject: [PATCH 054/148] Request tests (#31) * wrote tests: EmitProofRequest, IncrementID, HasRequest, GetRequest * fixed MarkNewHeaviest JSON * json structure for checkRequests * set digest * fixed MarkNewHeaviest * streamlined error testing * TestHandleNewRequest * Added RequestID type * fixed error code * added TestQueryGetRequest * finished TestQueryGetRequest * another test case for queryGetRequest * added more test cases to request.go * covered part of checkRequests tests * halfway through checkRequests test * added more test cases for requests.go * deleted todos * another test case for handler.go * deleted old code * fixed linting errors * expected then actual, ID not Id * added utils function requestIDFromString, added length check to beginning of NewRequestID * fixed bug in checkRequests * added output script note * more output script comments * combined RequestIDFromHex and RequestIDFromString --- golang/x/relay/alias.go | 3 + golang/x/relay/client/cli/query.go | 10 +- golang/x/relay/client/rest/query.go | 6 +- golang/x/relay/keeper/chain_test.go | 75 +++++----- golang/x/relay/keeper/handler_test.go | 42 ++++-- golang/x/relay/keeper/headers_test.go | 8 +- golang/x/relay/keeper/keeper_test.go | 62 +++++--- golang/x/relay/keeper/links_test.go | 12 +- golang/x/relay/keeper/querier_test.go | 83 ++++++++--- golang/x/relay/keeper/request_test.go | 200 ++++++++++++++++++++++++++ golang/x/relay/keeper/requests.go | 97 ++++++++----- golang/x/relay/types/errors.go | 16 ++- golang/x/relay/types/events.go | 2 +- golang/x/relay/types/keys.go | 2 +- golang/x/relay/types/querier.go | 2 +- golang/x/relay/types/requests.go | 52 +++++++ golang/x/relay/types/types.go | 3 + testVectors.json | 48 ++++++- 18 files changed, 572 insertions(+), 151 deletions(-) create mode 100644 golang/x/relay/keeper/request_test.go diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index 484b5194..19558db9 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -48,6 +48,9 @@ type ( // RawHeader is an 80-byte raw header RawHeader = types.RawHeader + // HexBytes is a type alias to make JSON hex ser/deser easier + HexBytes = types.HexBytes + // BitcoinHeader is a parsed Bitcoin header BitcoinHeader = types.BitcoinHeader diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index 99b149f7..b016823f 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -324,14 +324,14 @@ func GetCmdGetRequest(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "getrequest ", Example: "getrequest 12", - Long: "Get a proof request using the associated ID", + Long: "Get a proof request using the associated ID. ID can be an\n\"0x\" prepended hexbyte string or an integer", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - id, err := strconv.ParseUint(args[0], 10, 64) - if err != nil { - return err + id, idErr := types.RequestIDFromString(args[0]) + if idErr != nil { + return idErr } params := types.QueryParamsGetRequest{ @@ -347,7 +347,7 @@ func GetCmdGetRequest(queryRoute string, cdc *codec.Codec) *cobra.Command { res, _, err := cliCtx.QueryWithData("custom/relay/getrequest", queryData) if err != nil { - fmt.Printf("could not find request associated with id: %d... \n", id) + fmt.Printf("could not find request associated with id: %s... \n", args[0]) return nil } diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index 64a1d001..22cc57cf 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -263,9 +263,9 @@ func getRequestHandler(cliCtx context.CLIContext, storeName string) http.Handler return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - id, err := strconv.ParseUint(vars["id"], 10, 64) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + id, sdkErr := types.RequestIDFromString(vars["id"]) + if sdkErr != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, sdkErr.Error()) return } diff --git a/golang/x/relay/keeper/chain_test.go b/golang/x/relay/keeper/chain_test.go index 44dc1fa7..ed402d20 100644 --- a/golang/x/relay/keeper/chain_test.go +++ b/golang/x/relay/keeper/chain_test.go @@ -13,7 +13,7 @@ func (s *KeeperSuite) TestEmitReorg() { events := s.Context.EventManager().Events() e := events[0] - s.Equal(e.Type, "reorg") + s.Equal("reorg", e.Type) } func (s *KeeperSuite) TestGetDigestByStoreKey() { @@ -87,30 +87,24 @@ func (s *KeeperSuite) TestHeaviestFromAncestor() { s.SDKNil(err) for i := range tv.TestCases { + heaviest, err := s.Keeper.HeaviestFromAncestor( + s.Context, + tv.TestCases[i].Ancestor, + tv.TestCases[i].CurrentBest, + tv.TestCases[i].NewBest, + tv.TestCases[i].Limit) if tv.TestCases[i].Error == 0 { - heaviest, err := s.Keeper.HeaviestFromAncestor( - s.Context, - tv.TestCases[i].Ancestor, - tv.TestCases[i].CurrentBest, - tv.TestCases[i].NewBest, - tv.TestCases[i].Limit) s.SDKNil(err) s.Equal(heaviest, tv.TestCases[i].Output) } else { - _, err = s.Keeper.HeaviestFromAncestor( - s.Context, - tv.TestCases[i].Ancestor, - tv.TestCases[i].CurrentBest, - tv.TestCases[i].NewBest, - tv.TestCases[i].Limit) - s.Equal(err.Code(), sdk.CodeType(tv.TestCases[i].Error)) + s.Equal(sdk.CodeType(tv.TestCases[i].Error), err.Code()) } } } func (s *KeeperSuite) TestMarkNewHeaviest() { tv := s.Fixtures.ChainTestCases.IsMostRecentCA - tc := s.Fixtures.ChainTestCases.MarkNewHeaviest.TestCases + tc := s.Fixtures.ChainTestCases.MarkNewHeaviest pre := tv.PreRetargetChain post := tv.PostRetargetChain var postWithOrphan []types.BitcoinHeader @@ -127,7 +121,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { pre[1].Raw, 10, ) - s.EqualError(err, 103) + s.Equal(sdk.CodeType(103), err.Code()) err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) @@ -136,32 +130,6 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) s.SDKNil(err) - for i := range tc { - if tc[i].Error == 0 { - // updates the best known and emits an event - err = s.Keeper.MarkNewHeaviest( - s.Context, - tc[i].Ancestor, - tc[i].CurrentBest, - tc[i].NewBest, - tc[i].Limit, - ) - s.SDKNil(err) - events := s.Context.EventManager().Events() - e := events[0] - s.Equal(e.Type, tc[i].Output) - } else { - err = s.Keeper.MarkNewHeaviest( - s.Context, - tc[i].Ancestor, - tc[i].CurrentBest, - tc[i].NewBest, - tc[i].Limit, - ) - s.Equal(err.Code(), sdk.CodeType(tc[i].Error)) - } - } - // errors if the ancestor is not the heaviest common ancestor err = s.Keeper.MarkNewHeaviest( s.Context, @@ -178,5 +146,26 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { pre[1].Raw, 10, ) - s.Equal(err.Code(), sdk.CodeType(404)) + s.Equal(sdk.CodeType(404), err.Code()) + + for i := range tc { + s.Keeper.setBestKnownDigest(s.Context, tc[i].BestKnownDigest) + // updates the best known and emits an event + err = s.Keeper.MarkNewHeaviest( + s.Context, + tc[i].Ancestor, + tc[i].CurrentBest, + tc[i].NewBest, + tc[i].Limit, + ) + + if tc[i].Error == 0 { + s.SDKNil(err) + events := s.Context.EventManager().Events() + e := events[i] + s.Equal(tc[i].Output, e.Type) + } else { + s.Equal(sdk.CodeType(tc[i].Error), err.Code()) + } + } } diff --git a/golang/x/relay/keeper/handler_test.go b/golang/x/relay/keeper/handler_test.go index baa190f5..f2c9bfcc 100644 --- a/golang/x/relay/keeper/handler_test.go +++ b/golang/x/relay/keeper/handler_test.go @@ -1,6 +1,8 @@ package keeper import ( + "bytes" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -33,7 +35,7 @@ func (s *KeeperSuite) TestNewHandler() { } res := handler(s.Context, badMsg) - s.Equal(res.Log, "{\"codespace\":\"sdk\",\"code\":6,\"message\":\"Unrecognized relay Msg type: bad_message\"}") + s.Equal("{\"codespace\":\"sdk\",\"code\":6,\"message\":\"Unrecognized relay Msg type: bad_message\"}", res.Log) } func (s *KeeperSuite) TestHandleMsgIngestHeaderChain() { @@ -43,11 +45,11 @@ func (s *KeeperSuite) TestHandleMsgIngestHeaderChain() { newMsg := types.NewMsgIngestHeaderChain(getAccAddress(), testCases[0].Headers) res := handler(s.Context, newMsg) - s.Equal(res.Code, sdk.CodeType(103)) + s.Equal(sdk.CodeType(103), res.Code) s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) res = handler(s.Context, newMsg) - s.Equal(res.Events[0].Type, "extension") + s.Equal("extension", res.Events[0].Type) } func (s *KeeperSuite) TestHandleMsgIngestDifficultyChange() { @@ -57,12 +59,12 @@ func (s *KeeperSuite) TestHandleMsgIngestDifficultyChange() { newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) res := handler(s.Context, newMsg) - s.Equal(res.Code, sdk.CodeType(103)) + s.Equal(sdk.CodeType(103), res.Code) s.Keeper.ingestHeader(s.Context, testCases[0].PrevEpochStart) s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) res = handler(s.Context, newMsg) - s.Equal(res.Events[0].Type, "extension") + s.Equal("extension", res.Events[0].Type) } func (s *KeeperSuite) TestHandleMsgMarkNewHeaviest() { @@ -73,7 +75,7 @@ func (s *KeeperSuite) TestHandleMsgMarkNewHeaviest() { s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) res := handler(s.Context, newMsg) - s.Equal(res.Events[0].Type, "extension") + s.Equal("extension", res.Events[0].Type) } func (s *KeeperSuite) TestHandleMarkNewHeaviest() { @@ -99,10 +101,34 @@ func (s *KeeperSuite) TestHandleMarkNewHeaviest() { // returns correct error newMsg := types.NewMsgMarkNewHeaviest(getAccAddress(), tv.OldPeriodStart.HashLE, tv.OldPeriodStart.Raw, tv.OldPeriodStart.Raw, 10) res := handler(s.Context, newMsg) - s.Equal(res.Code, sdk.CodeType(403)) + s.Equal(sdk.CodeType(403), res.Code) // Successfully marks new heaviest newMsg = types.NewMsgMarkNewHeaviest(getAccAddress(), tv.Genesis.HashLE, tv.Genesis.Raw, pre[0].Raw, 10) res = handler(s.Context, newMsg) - s.Equal(res.Events[0].Type, "extension") + s.Equal("extension", res.Events[0].Type) +} + +func (s *KeeperSuite) TestHandleNewRequest() { + handler := NewHandler(s.Keeper) + + // Success + newRequest := types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0) + res := handler(s.Context, newRequest) + hasRequest := s.Keeper.hasRequest(s.Context, types.RequestID{}) + s.Equal(true, hasRequest) + s.Equal("proof_request", res.Events[0].Type) + + // Msg validation failed + newRequest = types.NewMsgNewRequest(getAccAddress(), []byte{0}, []byte{0}, 0, 0) + res = handler(s.Context, newRequest) + s.Equal(sdk.CodeType(602), res.Code) + + // setRequest error + store := s.Keeper.getRequestStore(s.Context) + store.Set([]byte(types.RequestIDTag), []byte("badID")) + + newRequest = types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0) + res = handler(s.Context, newRequest) + s.Equal(sdk.CodeType(107), res.Code) } diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index e3099278..fcf7c97f 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -16,7 +16,7 @@ func (s *KeeperSuite) TestEmitExtension() { events := s.Context.EventManager().Events() e := events[0] - s.Equal(e.Type, "extension") + s.Equal("extension", e.Type) } func (s *KeeperSuite) TestValidateHeaderChain() { @@ -81,10 +81,10 @@ func (s *KeeperSuite) TestIngestHeader() { for _, tc := range cases { s.Keeper.ingestHeader(s.Context, tc.Headers[0]) hasHeader := s.Keeper.HasHeader(s.Context, tc.Headers[0].HashLE) - s.Equal(hasHeader, true) + s.Equal(true, hasHeader) header, err := s.Keeper.GetHeader(s.Context, tc.Headers[0].HashLE) s.SDKNil(err) - s.Equal(header, tc.Headers[0]) + s.Equal(tc.Headers[0], header) } } @@ -134,6 +134,6 @@ func (s *KeeperSuite) TestCompareTargets() { for _, tc := range cases { result := compareTargets(tc.Full, tc.Truncated) - s.Equal(result, tc.Output) + s.Equal(tc.Output, result) } } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 28587b1e..63b71b6b 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -97,22 +97,19 @@ type HeaviestFromAncestor struct { } type NewHeaviestTestCase struct { - Ancestor types.Hash256Digest `json:"ancestor"` - CurrentBest types.RawHeader `json:"currentBest"` - NewBest types.RawHeader `json:"newBest"` - Limit uint32 `json:"limit"` - Error int `json:"error"` - Output string `json:"output"` -} - -type MarkNewHeaviest struct { - TestCases []NewHeaviestTestCase `json:"testCases"` + BestKnownDigest types.Hash256Digest `json:"bestKnownDigest"` + Ancestor types.Hash256Digest `json:"ancestor"` + CurrentBest types.RawHeader `json:"currentBest"` + NewBest types.RawHeader `json:"newBest"` + Limit uint32 `json:"limit"` + Error int `json:"error"` + Output string `json:"output"` } type ChainTestCases struct { - IsMostRecentCA IsMostRecentCA `json:"isMostRecentCommonAncestor"` - HeaviestFromAncestor HeaviestFromAncestor `json:"heaviestFromAncestor"` - MarkNewHeaviest MarkNewHeaviest `json:"markNewHeaviest"` + IsMostRecentCA IsMostRecentCA `json:"isMostRecentCommonAncestor"` + HeaviestFromAncestor HeaviestFromAncestor `json:"heaviestFromAncestor"` + MarkNewHeaviest []NewHeaviestTestCase `json:"markNewHeaviest"` } /***** HEADER TEST CASES *****/ @@ -146,11 +143,42 @@ type HeaderTestCases struct { CompareTargets []CompareCase `json:"compareTargets"` } +/***** Validator TEST CASES *****/ +type ValidateProofTestCase struct { + Proof types.SPVProof `json:"proof"` + BestKnown types.Hash256Digest `json:"bestKnown"` + LCA types.Hash256Digest `json:"lca"` + Error int `json:"error"` + Output bool `json:"output"` +} + +type ValidatorTestCases struct { + ValidateProof []ValidateProofTestCase `json:"validateProof"` +} + +/***** Request TEST CASES *****/ +type CheckRequestTestCase struct { + InputIdx uint8 `json:"inputIndex"` + OutputIdx uint8 `json:"outputIndex"` + Vin types.HexBytes `json:"vin"` + Vout types.HexBytes `json:"vout"` + RequestID types.HexBytes `json:"requestID"` + Error int `json:"error"` + Output bool `json:"output"` +} + +type RequestTestCases struct { + EmptyRequest types.ProofRequest `json:"emptyRequest"` + CheckRequests []CheckRequestTestCase `json:"checkRequests"` +} + /***** KEEPER TEST CASES *****/ type KeeperTestCases struct { - LinkTestCases LinkTestCases `json:"link"` - HeaderTestCases HeaderTestCases `json:"header"` - ChainTestCases ChainTestCases `json:"chain"` + LinkTestCases LinkTestCases `json:"link"` + HeaderTestCases HeaderTestCases `json:"header"` + ChainTestCases ChainTestCases `json:"chain"` + ValidatorTestCases ValidatorTestCases `json:"validator"` + RequestTestCases RequestTestCases `json:"requests"` } type KeeperSuite struct { @@ -243,7 +271,7 @@ func (suite *KeeperSuite) EqualError(e sdk.Error, code int) { if e.Code() != sdk.CodeType(code) { msg = fmt.Sprintf("%sExpected: %d\n", e.Error(), code) } - suite.Equal(e.Code(), sdk.CodeType(code), msg) + suite.Equal(sdk.CodeType(code), e.Code(), msg) } func (s *KeeperSuite) TestGetPrefixStore() { diff --git a/golang/x/relay/keeper/links_test.go b/golang/x/relay/keeper/links_test.go index 63b6bc6a..fba187ce 100644 --- a/golang/x/relay/keeper/links_test.go +++ b/golang/x/relay/keeper/links_test.go @@ -12,10 +12,9 @@ func (s *KeeperSuite) TestGetLink() { hasHeader := s.Keeper.hasLink(s.Context, child.HashLE) s.Equal(true, hasHeader) getHeader := s.Keeper.getLink(s.Context, child.HashLE) - s.Equal(getHeader, parent.HashLE) + s.Equal(parent.HashLE, getHeader) } -// TODO: Add test cases: add to JSON, loop over func (s *KeeperSuite) TestFindAncestor() { headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers anchor := s.Fixtures.HeaderTestCases.ValidateChain[0].Anchor @@ -23,22 +22,19 @@ func (s *KeeperSuite) TestFindAncestor() { // errors if link is not found _, err := s.Keeper.FindAncestor(s.Context, tc[0].Digest, tc[0].Offset) - s.Equal(err.Code(), types.CodeType(tc[0].Error)) + s.Equal(types.CodeType(tc[0].Error), err.Code()) s.Keeper.ingestHeader(s.Context, anchor) err = s.Keeper.IngestHeaderChain(s.Context, headers) s.SDKNil(err) for i := 1; i < len(tc); i++ { + ancestor, err := s.Keeper.FindAncestor(s.Context, tc[i].Digest, tc[i].Offset) if tc[i].Error == 0 { - // successfully retrieves ancestor - ancestor, err := s.Keeper.FindAncestor(s.Context, tc[i].Digest, tc[i].Offset) s.SDKNil(err) s.Equal(tc[i].Output, ancestor) } else { - // errors if link is not found - _, err = s.Keeper.FindAncestor(s.Context, tc[i].Digest, tc[i].Offset) - s.Equal(err.Code(), types.CodeType(tc[i].Error)) + s.Equal(types.CodeType(tc[i].Error), err.Code()) } } } diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index eb8b6197..e3b333b1 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -72,7 +72,7 @@ func (s *KeeperSuite) TestNewQuerier() { // Test that NewQuerier errors when given a bad path _, err := querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(6)) + s.Equal(sdk.CodeType(6), err.Code()) } func (s *KeeperSuite) TestQueryIsAncestor() { @@ -106,7 +106,7 @@ func (s *KeeperSuite) TestQueryIsAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, true) + s.Equal(true, result.Res) // If Limit is 0, it will use default limit params = types.QueryParamsIsAncestor{ @@ -126,7 +126,7 @@ func (s *KeeperSuite) TestQueryIsAncestor() { unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, true) + s.Equal(true, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -135,7 +135,7 @@ func (s *KeeperSuite) TestQueryIsAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(1)) + s.Equal(sdk.CodeType(1), err.Code()) } func (s *KeeperSuite) TestQueryGetRelayGenesis() { @@ -153,7 +153,7 @@ func (s *KeeperSuite) TestQueryGetRelayGenesis() { // Test that GetRelayGenesis errors if RelayGenesis is not found _, err := querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(105)) + s.Equal(sdk.CodeType(105), err.Code()) // Set Genesis state err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) @@ -168,7 +168,7 @@ func (s *KeeperSuite) TestQueryGetRelayGenesis() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, genesis.HashLE) + s.Equal(genesis.HashLE, result.Res) } func (s *KeeperSuite) TestQueryGetLastReorgLCA() { @@ -185,7 +185,7 @@ func (s *KeeperSuite) TestQueryGetLastReorgLCA() { // Test that it errors if it doesn't find LastReorgLCA _, err := querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(105)) + s.Equal(sdk.CodeType(105), err.Code()) setStateErr := s.Keeper.SetGenesisState(s.Context, genesis, epochStart) s.SDKNil(setStateErr) @@ -197,7 +197,7 @@ func (s *KeeperSuite) TestQueryGetLastReorgLCA() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, genesis.HashLE) + s.Equal(genesis.HashLE, result.Res) } func (s *KeeperSuite) TestQueryFindAncestor() { @@ -220,7 +220,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { // Test that it errors if ancestor is not found _, findAncestorErr := querier(s.Context, path, req) - s.Equal(findAncestorErr.Code(), sdk.CodeType(103)) + s.Equal(sdk.CodeType(103), findAncestorErr.Code()) // initialize data s.Keeper.ingestHeader(s.Context, anchor) @@ -236,7 +236,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) // s.SDKNil(unmarshallErr) s.Nil(unmarshallErr) - s.Equal(result.Res, headers[2].HashLE) + s.Equal(headers[2].HashLE, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -245,7 +245,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(1)) + s.Equal(sdk.CodeType(1), err.Code()) } func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { @@ -287,7 +287,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, headers[5].HashLE) + s.Equal(headers[5].HashLE, result.Res) // Test that it errors if HeaviestFromAncestorErrors params = types.QueryParamsHeaviestFromAncestor{ @@ -305,7 +305,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(103)) + s.Equal(sdk.CodeType(103), err.Code()) // Test that default limit is used if limit is set to zero params = types.QueryParamsHeaviestFromAncestor{ @@ -327,7 +327,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, headers[5].HashLE) + s.Equal(headers[5].HashLE, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -336,7 +336,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(1)) + s.Equal(sdk.CodeType(1), err.Code()) } func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { @@ -382,7 +382,7 @@ func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, true) + s.Equal(true, result.Res) // Test that it looks up the default limit if limit is set to zero params = types.QueryParamsIsMostRecentCommonAncestor{ @@ -404,7 +404,7 @@ func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(result.Res, true) + s.Equal(true, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -413,5 +413,52 @@ func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(err.Code(), sdk.CodeType(1)) + s.Equal(sdk.CodeType(1), err.Code()) +} + +func (s *KeeperSuite) TestQueryGetRequest() { + querier := NewQuerier(s.Keeper) + + path := []string{"getrequest"} + + // bad req + req := abci.RequestQuery{ + Path: "custom/relay/getrequest", + Data: []byte{0}, + } + + // Errors if it cannot unmarshal req data + _, err := querier(s.Context, path, req) + s.Equal(sdk.CodeType(1), err.Code()) + + // marshal params and set req + params := types.QueryParamsGetRequest{ + ID: types.RequestID{}, + } + marshalledParams, marshalErr := json.Marshal(params) + s.Nil(marshalErr) + + req = abci.RequestQuery{ + Path: "custom/relay/getrequest", + Data: marshalledParams, + } + + // Errors if request is not found + _, err = querier(s.Context, path, req) + s.Equal(sdk.CodeType(601), err.Code()) + + // Set Request + err = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + s.SDKNil(err) + + // Use querier handler to get request + res, err := querier(s.Context, path, req) + s.SDKNil(err) + + // Unmarshall the result and test + var result types.QueryResGetRequest + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) + s.Nil(unmarshallErr) + s.Equal(s.Fixtures.RequestTestCases.EmptyRequest, result.Res) } diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go new file mode 100644 index 00000000..5e3a30f7 --- /dev/null +++ b/golang/x/relay/keeper/request_test.go @@ -0,0 +1,200 @@ +package keeper + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func (s *KeeperSuite) TestEmitProofRequest() { + s.Keeper.emitProofRequest(s.Context, []byte{0}, []byte{0}, 0, types.RequestID{}) + + events := s.Context.EventManager().Events() + e := events[0] + s.Equal("proof_request", e.Type) +} + +// tests getNextID and incrementID +func (s *KeeperSuite) TestIncrementID() { + id, err := s.Keeper.getNextID(s.Context) + s.SDKNil(err) + s.Equal(types.RequestID{}, id) + + err = s.Keeper.incrementID(s.Context) + s.SDKNil(err) + + id, err = s.Keeper.getNextID(s.Context) + s.SDKNil(err) + s.Equal(types.RequestID{0, 0, 0, 0, 0, 0, 0, 1}, id) + + // errors if it cannot get next ID + store := s.Keeper.getRequestStore(s.Context) + idTag := []byte(types.RequestIDTag) + store.Set(idTag, bytes.Repeat([]byte{9}, 9)) + + err = s.Keeper.incrementID(s.Context) + s.Equal(sdk.CodeType(107), err.Code()) +} + +func (s *KeeperSuite) TestHasRequest() { + hasRequest := s.Keeper.hasRequest(s.Context, types.RequestID{}) + s.Equal(false, hasRequest) + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4) + s.Nil(requestErr) + hasRequest = s.Keeper.hasRequest(s.Context, types.RequestID{}) + s.Equal(true, hasRequest) +} + +func (s *KeeperSuite) TestSetRequest() { + store := s.Keeper.getRequestStore(s.Context) + idTag := []byte(types.RequestIDTag) + store.Set(idTag, bytes.Repeat([]byte{9}, 9)) + + err := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + s.Equal(sdk.CodeType(107), err.Code()) +} + +func (s *KeeperSuite) TestSetRequestState() { + // errors if request is not found + activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) + s.Equal(sdk.CodeType(601), activeErr.Code()) + + // set request + requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0) + s.Nil(requestErr) + // change active state to false + activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, false) + s.Nil(activeErr) + + deactivatedRequest, deactivatedRequestErr := s.Keeper.getRequest(s.Context, types.RequestID{}) + s.Nil(deactivatedRequestErr) + s.Equal(false, deactivatedRequest.ActiveState) +} + +func (s *KeeperSuite) TestGetRequest() { + requestRes := s.Fixtures.RequestTestCases.EmptyRequest + request, err := s.Keeper.getRequest(s.Context, types.RequestID{}) + s.Equal(sdk.CodeType(601), err.Code()) + s.Equal(types.ProofRequest{}, request) + + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + s.Nil(requestErr) + + request, err = s.Keeper.getRequest(s.Context, types.RequestID{}) + s.Nil(err) + s.Equal(requestRes, request) +} + +func (s *KeeperSuite) TestCheckRequests() { + tc := s.Fixtures.RequestTestCases.CheckRequests + v := tc[0] + + // Errors if request is not found + id, err := types.NewRequestID(v.RequestID[:]) + s.SDKNil(err) + valid, err := s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + id) + s.Equal(false, valid) + s.Equal(sdk.CodeType(601), err.Code()) + + // set request + requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0) + s.Nil(requestErr) + // change active state to false + activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) + s.Nil(activeErr) + // errors if request is not active + valid, err = s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + id) + s.Equal(false, valid) + s.Equal(sdk.CodeType(606), err.Code()) + + // change active state to false + activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, true) + s.Nil(activeErr) + // errors if request pays is not equal to output + valid, err = s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + id) + s.Equal(false, valid) + s.Equal(sdk.CodeType(607), err.Code()) + + // Errors if output value is less than pays value + out, outErr := btcspv.ExtractOutputAtIndex(v.Vout, v.OutputIdx) + s.Nil(outErr) + // out[8:] extracts the output script which we use to set the request + requestErr = s.Keeper.setRequest(s.Context, []byte{0}, out[8:], 1000, 0) + s.SDKNil(requestErr) + valid, err = s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + types.RequestID{0, 0, 0, 0, 0, 0, 0, 1}) + s.Equal(false, valid) + s.Equal(sdk.CodeType(608), err.Code()) + + // Errors if input value does not equal spends value + requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{0}, 0, 255) + s.SDKNil(requestErr) + valid, err = s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + types.RequestID{0, 0, 0, 0, 0, 0, 0, 2}) + s.Equal(false, valid) + s.Equal(sdk.CodeType(609), err.Code()) + + // Success + in := btcspv.ExtractInputAtIndex(v.Vin, v.InputIdx) + outpoint := btcspv.ExtractOutpoint(in) + // out[8:] extracts the output script which we use to set the request + requestErr = s.Keeper.setRequest(s.Context, outpoint, out[8:], 10, 255) + s.SDKNil(requestErr) + valid, err = s.Keeper.checkRequests( + s.Context, + v.InputIdx, + v.OutputIdx, + v.Vin, + v.Vout, + types.RequestID{0, 0, 0, 0, 0, 0, 0, 3}) + s.SDKNil(err) + s.Equal(true, valid) + + for i := 1; i < len(tc); i++ { + id, err := types.NewRequestID(tc[i].RequestID[:]) + s.SDKNil(err) + valid, err := s.Keeper.checkRequests( + s.Context, + tc[i].InputIdx, + tc[i].OutputIdx, + tc[i].Vin, + tc[i].Vout, + id) + s.Equal(tc[i].Output, valid) + if tc[i].Error == 0 { + s.SDKNil(err) + } else { + s.Equal(sdk.CodeType(tc[i].Error), err.Code()) + } + } +} diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 1e22d582..0cba84ff 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -1,6 +1,7 @@ package keeper import ( + "bytes" "encoding/binary" "encoding/json" @@ -10,7 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) emitProofRequest(ctx sdk.Context, pays, spends []byte, paysValue, id uint64) { +func (k Keeper) emitProofRequest(ctx sdk.Context, pays, spends []byte, paysValue uint64, id types.RequestID) { ctx.EventManager().EmitEvent(types.NewProofRequestEvent(pays, spends, paysValue, id)) } @@ -18,13 +19,9 @@ func (k Keeper) getRequestStore(ctx sdk.Context) sdk.KVStore { return k.getPrefixStore(ctx, types.RequestStorePrefix) } -func (k Keeper) hasRequest(ctx sdk.Context, id uint64) bool { - // convert id to bytes - idBytes := make([]byte, 8) - binary.BigEndian.PutUint64(idBytes, id) - +func (k Keeper) hasRequest(ctx sdk.Context, id types.RequestID) bool { store := k.getRequestStore(ctx) - return store.Has(idBytes) + return store.Has(id[:]) } func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValue uint64, numConfs uint8) sdk.Error { @@ -42,35 +39,55 @@ func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValu } // When a new request comes in, get the id and use it to store request - id := k.getNextID(ctx) - - buf, err := json.Marshal(request) + id, err := k.getNextID(ctx) if err != nil { + return err + } + + buf, marshalErr := json.Marshal(request) + if marshalErr != nil { return types.ErrMarshalJSON(types.DefaultCodespace) } - store.Set(id, buf) + store.Set(id[:], buf) // Increment the ID - k.incrementID(ctx) + incrementErr := k.incrementID(ctx) + if incrementErr != nil { + return incrementErr + } // Emit Proof Request event - numID := binary.BigEndian.Uint64(id) - k.emitProofRequest(ctx, pays, spends, request.PaysValue, numID) + k.emitProofRequest(ctx, pays, spends, request.PaysValue, id) return nil } -func (k Keeper) getRequest(ctx sdk.Context, id uint64) (types.ProofRequest, sdk.Error) { +func (k Keeper) setRequestState(ctx sdk.Context, requestID types.RequestID, active bool) sdk.Error { store := k.getRequestStore(ctx) + request, err := k.getRequest(ctx, requestID) + if err != nil { + return err + } - // convert id to bytes - idBytes := make([]byte, 8) - binary.BigEndian.PutUint64(idBytes, id) + request.ActiveState = active + + buf, marshalErr := json.Marshal(request) + if marshalErr != nil { + return types.ErrMarshalJSON(types.DefaultCodespace) + } + store.Set(requestID[:], buf) + return nil +} + +func (k Keeper) getRequest(ctx sdk.Context, id types.RequestID) (types.ProofRequest, sdk.Error) { + store := k.getRequestStore(ctx) hasRequest := k.hasRequest(ctx, id) if !hasRequest { return types.ProofRequest{}, types.ErrUnknownRequest(types.DefaultCodespace) } - buf := store.Get(idBytes) + + buf := store.Get(id[:]) + var request types.ProofRequest jsonErr := json.Unmarshal(buf, &request) if jsonErr != nil { @@ -79,30 +96,40 @@ func (k Keeper) getRequest(ctx sdk.Context, id uint64) (types.ProofRequest, sdk. return request, nil } -func (k Keeper) incrementID(ctx sdk.Context) { +func (k Keeper) incrementID(ctx sdk.Context) sdk.Error { store := k.getRequestStore(ctx) // get id - id := k.getNextID(ctx) + id, err := k.getNextID(ctx) + if err != nil { + return err + } // convert id to uint64 and add 1 - newID := binary.BigEndian.Uint64(id) + 1 + newID := binary.BigEndian.Uint64(id[:]) + 1 // convert back to bytes and store b := make([]byte, 8) binary.BigEndian.PutUint64(b, newID) - store.Set([]byte(types.RequestID), b) + store.Set([]byte(types.RequestIDTag), b) + // if no errors, return nil + return nil } // getNextID retrieves the ID. The ID is incremented after storing a request, // so this returns the next ID to be used. -func (k Keeper) getNextID(ctx sdk.Context) []byte { +func (k Keeper) getNextID(ctx sdk.Context) (types.RequestID, sdk.Error) { store := k.getRequestStore(ctx) - id := []byte(types.RequestID) - if !store.Has(id) { - store.Set(id, []byte{0}) + idTag := []byte(types.RequestIDTag) + if !store.Has(idTag) { + store.Set(idTag, bytes.Repeat([]byte{0}, 8)) + } + id := store.Get(idTag) + newID, err := types.NewRequestID(id) + if err != nil { + return types.RequestID{}, err } - return store.Get(id) + return newID, nil } -func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID uint64) (bool, sdk.Error) { +func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID types.RequestID) (bool, sdk.Error) { if !btcspv.ValidateVin(vin) { return false, types.ErrInvalidVin(types.DefaultCodespace) } @@ -118,25 +145,27 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vi return false, types.ErrClosedRequest(types.DefaultCodespace) } - hasPays := req.Pays != types.Hash256Digest{} + hasPays := req.Pays != btcspv.Hash256([]byte{0}) if hasPays { // We can ignore this error because we know that ValidateVout passed out, _ := btcspv.ExtractOutputAtIndex(vout, outputIndex) + // hash the output script (out[8:]) outDigest := btcspv.Hash256(out[8:]) if outDigest != req.Pays { return false, types.ErrRequestPays(types.DefaultCodespace) } paysValue := req.PaysValue - if paysValue != 0 || uint64(btcspv.ExtractValue(out)) < paysValue { + if paysValue != 0 && uint64(btcspv.ExtractValue(out)) < paysValue { return false, types.ErrRequestValue(types.DefaultCodespace) } } - hasSpends := req.Spends != types.Hash256Digest{} + hasSpends := req.Spends != btcspv.Hash256([]byte{0}) if hasSpends { in := btcspv.ExtractInputAtIndex(vin, inputIndex) - inDigest := btcspv.Hash256(in) - if !hasSpends || inDigest != req.Spends { + outpoint := btcspv.ExtractOutpoint(in) + inDigest := btcspv.Hash256(outpoint) + if hasSpends && inDigest != req.Spends { return false, types.ErrRequestSpends(types.DefaultCodespace) } } diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 71363e76..7c62dd9d 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -45,12 +47,17 @@ const ( // BadHexMessage is the corresponding message BadHexMessage = "Bad hex string in query or msg" + // BadHexLen occurs when a hex argument is the wrong length + BadHexLen sdk.CodeType = 107 + // BadHexLenMessage is the corresponding message + BadHexLenMessage = "Expected %d bytes in a RequestID, got %d" + // BitcoinSPV is the code for errors bubbled up from Bitcoin SPV - BitcoinSPV sdk.CodeType = 107 + BitcoinSPV sdk.CodeType = 108 // BitcoinSPVMessage is the corresponding message // AlreadyInit is the code for a second attempt to init the relay - AlreadyInit sdk.CodeType = 108 + AlreadyInit sdk.CodeType = 109 // AlreadyInitMessage is the corresponding message AlreadyInitMessage = "Relay has already set genesis state" @@ -255,6 +262,11 @@ func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, BadHex, BadHexMessage) } +// ErrBadHexLen throws an error +func ErrBadHexLen(codespace sdk.CodespaceType, expected, actual int) sdk.Error { + return sdk.NewError(codespace, BadHexLen, fmt.Sprintf(BadHexLenMessage, expected, actual)) +} + // ErrAlreadyInit throws an error func ErrAlreadyInit(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, AlreadyInit, AlreadyInitMessage) diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 8241e6ba..48ad3a2c 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -46,7 +46,7 @@ func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { } // NewExtensionEvent instantiates a proof request event -func NewProofRequestEvent(pays, spends []byte, paysValue, id uint64) sdk.Event { +func NewProofRequestEvent(pays, spends []byte, paysValue uint64, id RequestID) sdk.Event { return sdk.NewEvent( EventTypeProofRequest, sdk.NewAttribute(AttributeKeyRequestID, fmt.Sprintf("%d", id)), diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index b57d81d9..a5e3a7dd 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -28,5 +28,5 @@ const ( // LastReorgLCAStorage is the storage key for the last reorg LCA LastReorgLCAStorage = "LastReorgLCA" - RequestID = "id" + RequestIDTag = "id" ) diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 0690e513..f2dfd2e5 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -145,7 +145,7 @@ func (r QueryResIsMostRecentCommonAncestor) String() string { } type QueryParamsGetRequest struct { - ID uint64 `json:"id"` + ID RequestID `json:"id"` } // QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go index 979e1bb8..13c0bc88 100644 --- a/golang/x/relay/types/requests.go +++ b/golang/x/relay/types/requests.go @@ -1,5 +1,17 @@ package types +import ( + "encoding/binary" + "encoding/hex" + "strconv" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// RequestID is an 8 byte id used to store requests +type RequestID [8]byte + +// ProofRequest is info about a proof request type ProofRequest struct { Spends Hash256Digest `json:"spends"` Pays Hash256Digest `json:"pays"` @@ -7,3 +19,43 @@ type ProofRequest struct { ActiveState bool `json:"activeState"` NumConfs uint8 `json:"numConfs"` } + +// NewRequestID instantiates a RequestID from a byte slice +func NewRequestID(b []byte) (RequestID, sdk.Error) { + if len(b) != 8 { + return RequestID{}, ErrBadHexLen(DefaultCodespace, 8, len(b)) + } + var h RequestID + copied := copy(h[:], b) + if copied != 8 { + return RequestID{}, ErrBadHexLen(DefaultCodespace, 8, copied) + } + return h, nil +} + +// RequestIDFromString converts a hex string or integer string into a RequestID +func RequestIDFromString(s string) (RequestID, error) { + var idBytes []byte + var err error + + if s[:2] == "0x" { + idBytes, err = hex.DecodeString(s[2:]) + if err != nil { + return RequestID{}, ErrBadHex(DefaultCodespace) + } + } else { + id, parseErr := strconv.ParseUint(s, 10, 64) + if parseErr != nil { + return RequestID{}, parseErr + } + + // convert to bytes + binary.BigEndian.PutUint64(idBytes, id) + } + + requestID, newIDErr := NewRequestID(idBytes) + if newIDErr != nil { + return RequestID{}, newIDErr + } + return requestID, err +} diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 584fe0ba..878f576b 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -17,6 +17,9 @@ type Hash160Digest = btcspv.Hash160Digest // RawHeader is an 80-byte raw header type RawHeader = btcspv.RawHeader +// HexBytes is a type alias to make JSON hex ser/deser easier +type HexBytes = btcspv.HexBytes + // BitcoinHeader is a parsed Bitcoin header type BitcoinHeader = btcspv.BitcoinHeader diff --git a/testVectors.json b/testVectors.json index 9803bbac..ccf59b02 100644 --- a/testVectors.json +++ b/testVectors.json @@ -617,7 +617,7 @@ }, "internal": false, "isMainnet": true, - "output": 107 + "output": 108 }, { "comment": "header chain doesn't pass validation, anchor is low work", @@ -655,7 +655,7 @@ }, "internal": false, "isMainnet": true, - "output": 107 + "output": 108 } ], "compareTargets": [ @@ -1161,10 +1161,9 @@ "error": 0, "output": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000" }] - } - }, - "markNewHeaviest": { - "testCases": [{ + }, + "markNewHeaviest": [{ + "bestKnownDigest": "0x8b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", "ancestor": "0x9b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", "currentBest": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", "newBest": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", @@ -1172,6 +1171,7 @@ "error": 403, "output": "" }, { + "bestKnownDigest": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", "ancestor": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", "currentBest": "0x00000020d0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f200000000000000000005e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594af3fe1d58d2550418e90ec632", "newBest": "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", @@ -1179,6 +1179,7 @@ "error": 103, "output": "" }, { + "bestKnownDigest": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", "ancestor": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", "currentBest": "0x0000002016d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f5b001e58d2550418183b1b41", "newBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", @@ -1186,6 +1187,7 @@ "error": 0, "output": "extension" }, { + "bestKnownDigest": "0xf24d697743f1fce5857ee6f7d709ed60830ae4239da71d010000000000000000", "ancestor": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", "currentBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", "newBest": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000f7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c073347161e58745104186cff9846", @@ -1193,5 +1195,39 @@ "error": 405, "output": "" }] + }, + "requests": { + "emptyRequest": { + "spends": "0x1406e05881e299367766d313e26c05564ec91bf721d31726bd6e46e60689539a", + "pays": "0x1406e05881e299367766d313e26c05564ec91bf721d31726bd6e46e60689539a", + "paysValue": 0, + "activeState": true, + "numConfs": 0 + }, + "checkRequests": [{ + "inputIndex": 0, + "outputIndex": 1, + "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "requestID": "0x0000000000000000", + "error": 605, + "output": false + }, { + "inputIndex": 1, + "outputIndex": 2, + "vin": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffff", + "vout": "0x02", + "requestID": "0x0100000000000000", + "error": 604, + "output": false + }, { + "inputIndex": 1, + "outputIndex": 2, + "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x004897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", + "requestID": "0x0100000000000000", + "error": 605, + "output": false + }] } } From 55fc0b4db685761ae8620f73e5b49078074720e3 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Wed, 29 Jan 2020 14:53:38 -0700 Subject: [PATCH 055/148] Proof Validator (#24) * wrote validateProof function, added test file and valid case test vectors * finished test for validator function * added confs check todo * added getConfs function and numConfs check * updated validator and tests * fixed byte to uint64 conversions * finished validator.go and 100% test coverage * changed back uint64 conversion * wrote checkRequestsFilled * deleted old code * fixed unmarshalling of RequestID * Added valid test case to TestCheckRequestsFilled * finished checkRequestsFilled test * fixed comment * streamlined validate/check functions * updated checkRequestsFilled * finished rewriting validator functions and tests * more descriptive naming --- golang/x/relay/keeper/keeper_test.go | 24 ++-- golang/x/relay/keeper/request_test.go | 33 ++--- golang/x/relay/keeper/requests.go | 18 +-- golang/x/relay/keeper/validator.go | 75 ++++++++++++ golang/x/relay/keeper/validator_test.go | 108 +++++++++++++++++ golang/x/relay/types/errors.go | 20 ++++ golang/x/relay/types/requests.go | 24 ++++ golang/x/relay/types/utils.go | 11 ++ golang/x/relay/types/validator.go | 12 ++ testVectors.json | 153 +++++++++++++++++++++++- 10 files changed, 431 insertions(+), 47 deletions(-) create mode 100644 golang/x/relay/keeper/validator.go create mode 100644 golang/x/relay/keeper/validator_test.go create mode 100644 golang/x/relay/types/utils.go create mode 100644 golang/x/relay/types/validator.go diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 63b71b6b..b794386b 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -146,25 +146,29 @@ type HeaderTestCases struct { /***** Validator TEST CASES *****/ type ValidateProofTestCase struct { Proof types.SPVProof `json:"proof"` - BestKnown types.Hash256Digest `json:"bestKnown"` + BestKnown types.BitcoinHeader `json:"bestKnown"` LCA types.Hash256Digest `json:"lca"` Error int `json:"error"` - Output bool `json:"output"` +} + +type CheckRequestsFilledTestCase struct { + FilledRequests types.FilledRequests `json:"filledRequest"` + Error int `json:"error"` } type ValidatorTestCases struct { - ValidateProof []ValidateProofTestCase `json:"validateProof"` + ValidateProof []ValidateProofTestCase `json:"validateProof"` + CheckRequestsFilled []CheckRequestsFilledTestCase `json:"checkRequestsFilled"` } /***** Request TEST CASES *****/ type CheckRequestTestCase struct { - InputIdx uint8 `json:"inputIndex"` - OutputIdx uint8 `json:"outputIndex"` - Vin types.HexBytes `json:"vin"` - Vout types.HexBytes `json:"vout"` - RequestID types.HexBytes `json:"requestID"` - Error int `json:"error"` - Output bool `json:"output"` + InputIdx uint8 `json:"inputIndex"` + OutputIdx uint8 `json:"outputIndex"` + Vin types.HexBytes `json:"vin"` + Vout types.HexBytes `json:"vout"` + RequestID types.RequestID `json:"requestID"` + Error int `json:"error"` } type RequestTestCases struct { diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go index 5e3a30f7..1eafb4aa 100644 --- a/golang/x/relay/keeper/request_test.go +++ b/golang/x/relay/keeper/request_test.go @@ -92,16 +92,13 @@ func (s *KeeperSuite) TestCheckRequests() { v := tc[0] // Errors if request is not found - id, err := types.NewRequestID(v.RequestID[:]) - s.SDKNil(err) - valid, err := s.Keeper.checkRequests( + err := s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, v.Vin, v.Vout, - id) - s.Equal(false, valid) + v.RequestID) s.Equal(sdk.CodeType(601), err.Code()) // set request @@ -111,28 +108,26 @@ func (s *KeeperSuite) TestCheckRequests() { activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) s.Nil(activeErr) // errors if request is not active - valid, err = s.Keeper.checkRequests( + err = s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, v.Vin, v.Vout, - id) - s.Equal(false, valid) + v.RequestID) s.Equal(sdk.CodeType(606), err.Code()) // change active state to false activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, true) s.Nil(activeErr) // errors if request pays is not equal to output - valid, err = s.Keeper.checkRequests( + err = s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, v.Vin, v.Vout, - id) - s.Equal(false, valid) + v.RequestID) s.Equal(sdk.CodeType(607), err.Code()) // Errors if output value is less than pays value @@ -141,27 +136,25 @@ func (s *KeeperSuite) TestCheckRequests() { // out[8:] extracts the output script which we use to set the request requestErr = s.Keeper.setRequest(s.Context, []byte{0}, out[8:], 1000, 0) s.SDKNil(requestErr) - valid, err = s.Keeper.checkRequests( + err = s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, v.Vin, v.Vout, types.RequestID{0, 0, 0, 0, 0, 0, 0, 1}) - s.Equal(false, valid) s.Equal(sdk.CodeType(608), err.Code()) // Errors if input value does not equal spends value requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{0}, 0, 255) s.SDKNil(requestErr) - valid, err = s.Keeper.checkRequests( + err = s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, v.Vin, v.Vout, types.RequestID{0, 0, 0, 0, 0, 0, 0, 2}) - s.Equal(false, valid) s.Equal(sdk.CodeType(609), err.Code()) // Success @@ -170,7 +163,7 @@ func (s *KeeperSuite) TestCheckRequests() { // out[8:] extracts the output script which we use to set the request requestErr = s.Keeper.setRequest(s.Context, outpoint, out[8:], 10, 255) s.SDKNil(requestErr) - valid, err = s.Keeper.checkRequests( + err = s.Keeper.checkRequests( s.Context, v.InputIdx, v.OutputIdx, @@ -178,19 +171,15 @@ func (s *KeeperSuite) TestCheckRequests() { v.Vout, types.RequestID{0, 0, 0, 0, 0, 0, 0, 3}) s.SDKNil(err) - s.Equal(true, valid) for i := 1; i < len(tc); i++ { - id, err := types.NewRequestID(tc[i].RequestID[:]) - s.SDKNil(err) - valid, err := s.Keeper.checkRequests( + err := s.Keeper.checkRequests( s.Context, tc[i].InputIdx, tc[i].OutputIdx, tc[i].Vin, tc[i].Vout, - id) - s.Equal(tc[i].Output, valid) + tc[i].RequestID) if tc[i].Error == 0 { s.SDKNil(err) } else { diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 0cba84ff..0b3c0e7a 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -129,20 +129,20 @@ func (k Keeper) getNextID(ctx sdk.Context) (types.RequestID, sdk.Error) { return newID, nil } -func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID types.RequestID) (bool, sdk.Error) { +func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID types.RequestID) sdk.Error { if !btcspv.ValidateVin(vin) { - return false, types.ErrInvalidVin(types.DefaultCodespace) + return types.ErrInvalidVin(types.DefaultCodespace) } if !btcspv.ValidateVout(vout) { - return false, types.ErrInvalidVout(types.DefaultCodespace) + return types.ErrInvalidVout(types.DefaultCodespace) } req, reqErr := k.getRequest(ctx, requestID) if reqErr != nil { - return false, reqErr + return reqErr } if !req.ActiveState { - return false, types.ErrClosedRequest(types.DefaultCodespace) + return types.ErrClosedRequest(types.DefaultCodespace) } hasPays := req.Pays != btcspv.Hash256([]byte{0}) @@ -152,11 +152,11 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vi // hash the output script (out[8:]) outDigest := btcspv.Hash256(out[8:]) if outDigest != req.Pays { - return false, types.ErrRequestPays(types.DefaultCodespace) + return types.ErrRequestPays(types.DefaultCodespace) } paysValue := req.PaysValue if paysValue != 0 && uint64(btcspv.ExtractValue(out)) < paysValue { - return false, types.ErrRequestValue(types.DefaultCodespace) + return types.ErrRequestValue(types.DefaultCodespace) } } @@ -166,8 +166,8 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vi outpoint := btcspv.ExtractOutpoint(in) inDigest := btcspv.Hash256(outpoint) if hasSpends && inDigest != req.Spends { - return false, types.ErrRequestSpends(types.DefaultCodespace) + return types.ErrRequestSpends(types.DefaultCodespace) } } - return true, nil + return nil } diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go new file mode 100644 index 00000000..42c5100c --- /dev/null +++ b/golang/x/relay/keeper/validator.go @@ -0,0 +1,75 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func (k Keeper) getConfs(ctx sdk.Context, header types.BitcoinHeader) (uint32, sdk.Error) { + bestKnown, err := k.GetBestKnownDigest(ctx) + if err != nil { + return 0, err + } + bestKnownHeader, err := k.GetHeader(ctx, bestKnown) + if err != nil { + return 0, err + } + return bestKnownHeader.Height - header.Height, nil +} + +func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { + // If it is not valid, it will return an error + _, err := proof.Validate() + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } + + lca, lcaErr := k.GetLastReorgLCA(ctx) + if lcaErr != nil { + return lcaErr + } + isAncestor := k.IsAncestor(ctx, proof.ConfirmingHeader.HashLE, lca, 240) + if !isAncestor { + return types.ErrNotAncestor(types.DefaultCodespace) + } + + return nil +} + +func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.FilledRequests) sdk.Error { + // Validate Proof once + err := k.validateProof(ctx, filledRequests.Proof) + if err != nil { + return err + } + + confs, confsErr := k.getConfs(ctx, filledRequests.Proof.ConfirmingHeader) + if confsErr != nil { + return confsErr + } + + for i := range filledRequests.Filled { + // get request + request, getErr := k.getRequest(ctx, filledRequests.Filled[i].ID) + if getErr != nil { + return getErr + } + // check confirmations + if confs < uint32(request.NumConfs) { + return types.ErrNotEnoughConfs(types.DefaultCodespace) + } + + // check request + err := k.checkRequests( + ctx, + filledRequests.Filled[i].InputIndex, + filledRequests.Filled[i].OutputIndex, + filledRequests.Proof.Vin, + filledRequests.Proof.Vout, + filledRequests.Filled[i].ID) + if err != nil { + return err + } + } + return nil +} diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go new file mode 100644 index 00000000..3120d41c --- /dev/null +++ b/golang/x/relay/keeper/validator_test.go @@ -0,0 +1,108 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" +) + +func (s *KeeperSuite) TestGetConfs() { + header := s.Fixtures.ValidatorTestCases.ValidateProof[0].Proof.ConfirmingHeader + bestKnown := s.Fixtures.ValidatorTestCases.ValidateProof[0].BestKnown + + // errors if Best Known Digest is not found + confs, err := s.Keeper.getConfs(s.Context, header) + s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(uint32(0), confs) + + // errors if Best Known Digest header is not found + s.Keeper.setBestKnownDigest(s.Context, bestKnown.HashLE) + + confs, err = s.Keeper.getConfs(s.Context, header) + s.Equal(sdk.CodeType(103), err.Code()) + s.Equal(uint32(0), confs) + + // success + s.Keeper.ingestHeader(s.Context, bestKnown) + + confs, err = s.Keeper.getConfs(s.Context, header) + s.SDKNil(err) + s.Equal(uint32(4), confs) +} + +func (s *KeeperSuite) TestValidateProof() { + proofCases := s.Fixtures.ValidatorTestCases.ValidateProof + proof := proofCases[0].Proof + + // errors if LCA is not found + err := s.Keeper.validateProof(s.Context, proof) + s.Equal(sdk.CodeType(105), err.Code()) + + // errors if link is not found + s.Keeper.setLastReorgLCA(s.Context, proofCases[0].LCA) + + err = s.Keeper.validateProof(s.Context, proof) + s.Equal(sdk.CodeType(610), err.Code()) + + for i := range proofCases { + // Store lots of stuff + s.Keeper.setLastReorgLCA(s.Context, proofCases[i].LCA) + s.Keeper.ingestHeader(s.Context, proofCases[i].Proof.ConfirmingHeader) + s.Keeper.setLink(s.Context, proofCases[i].Proof.ConfirmingHeader) + + if proofCases[i].Error != 0 { + err := s.Keeper.validateProof(s.Context, proofCases[i].Proof) + s.Equal(sdk.CodeType(proofCases[i].Error), err.Code()) + } else { + err := s.Keeper.validateProof(s.Context, proofCases[i].Proof) + s.Nil(err) + } + } +} + +func (s *KeeperSuite) TestCheckRequestsFilled() { + tc := s.Fixtures.ValidatorTestCases.CheckRequestsFilled + validProof := s.Fixtures.ValidatorTestCases.ValidateProof[0] + + s.Keeper.setLastReorgLCA(s.Context, validProof.LCA) + s.Keeper.ingestHeader(s.Context, validProof.Proof.ConfirmingHeader) + s.Keeper.setLink(s.Context, validProof.Proof.ConfirmingHeader) + s.Keeper.ingestHeader(s.Context, validProof.BestKnown) + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4) + s.Nil(requestErr) + + // errors if getConfs fails + err := s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) + s.Equal(sdk.CodeType(105), err.Code()) + + s.Keeper.setBestKnownDigest(s.Context, validProof.BestKnown.HashLE) + + // errors if checkRequest errors + // deactivate request + activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) + s.SDKNil(activeErr) + + err = s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) + s.Equal(sdk.CodeType(606), err.Code()) + + // reactivate request + activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, true) + s.SDKNil(activeErr) + + for i := range tc { + err := s.Keeper.checkRequestsFilled(s.Context, tc[i].FilledRequests) + if tc[i].Error != 0 { + s.Equal(sdk.CodeType(tc[i].Error), err.Code()) + } else { + s.SDKNil(err) + } + } + + // errors if number of confirmations is less than the number of confirmations on the request + requestErr = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 5) + s.Nil(requestErr) + + copiedRequest := tc[0].FilledRequests + copiedRequest.Filled[0].ID = types.RequestID{0, 0, 0, 0, 0, 0, 0, 1} + err = s.Keeper.checkRequestsFilled(s.Context, copiedRequest) + s.Equal(sdk.CodeType(611), err.Code()) +} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 7c62dd9d..0882c59c 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -177,6 +177,16 @@ const ( // RequestSpendsMessage is the corresponding message RequestSpendsMessage = "Does not match spends request" + // NotAncestor means the LCA is not an ancestor of the SPV Proof header + NotAncestor sdk.CodeType = 610 + // NotAncestorMessage is the corresponding message + NotAncestorMessage = "LCA not ancestor of proof header" + + // NotEnoughConfs means the proof does not have enough confirmations + NotEnoughConfs sdk.CodeType = 611 + // NotEnoughConfs is the corresponding message + NotEnoughConfsMessage = "Not enough confirmations" + // 700-block External ExternalError sdk.CodeType = 701 @@ -337,6 +347,16 @@ func ErrRequestSpends(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, RequestSpends, RequestSpendsMessage) } +// ErrNotAncestor throws an error +func ErrNotAncestor(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotAncestor, NotAncestorMessage) +} + +// ErrNotEnoughConfs throws an error +func ErrNotEnoughConfs(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, NotEnoughConfs, NotEnoughConfsMessage) +} + // ErrExternal converts any external error into an sdk error func ErrExternal(codespace sdk.CodespaceType, err error) sdk.Error { return sdk.NewError(codespace, ExternalError, err.Error()) diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go index 13c0bc88..af99c68b 100644 --- a/golang/x/relay/types/requests.go +++ b/golang/x/relay/types/requests.go @@ -3,6 +3,7 @@ package types import ( "encoding/binary" "encoding/hex" + "fmt" "strconv" sdk "github.com/cosmos/cosmos-sdk/types" @@ -59,3 +60,26 @@ func RequestIDFromString(s string) (RequestID, error) { } return requestID, err } + +// UnmarshalJSON unmarshalls 8 byte requestID +func (r *RequestID) UnmarshalJSON(b []byte) error { + // Have to trim quotation marks off byte array + buf, err := hex.DecodeString(strip0xPrefix(string(b[1 : len(b)-1]))) + if err != nil { + return err + } + + if len(buf) != 8 { + return fmt.Errorf("Expected 8 bytes, got %d bytes", len(buf)) + } + + copy(r[:], buf) + + return nil +} + +// MarshalJSON marashalls 8 byte RequestID as 0x-prepended hex +func (r RequestID) MarshalJSON() ([]byte, error) { + encoded := "\"0x" + hex.EncodeToString(r[:]) + "\"" + return []byte(encoded), nil +} diff --git a/golang/x/relay/types/utils.go b/golang/x/relay/types/utils.go new file mode 100644 index 00000000..87a769b9 --- /dev/null +++ b/golang/x/relay/types/utils.go @@ -0,0 +1,11 @@ +package types + +func strip0xPrefix(s string) string { + if len(s) < 2 { + return s + } + if s[0:2] == "0x" { + return s[2:] + } + return s +} diff --git a/golang/x/relay/types/validator.go b/golang/x/relay/types/validator.go new file mode 100644 index 00000000..22e189ff --- /dev/null +++ b/golang/x/relay/types/validator.go @@ -0,0 +1,12 @@ +package types + +type FilledRequestInfo struct { + InputIndex uint8 `json:"inputIndex"` + OutputIndex uint8 `json:"outputIndex"` + ID RequestID `json:"id"` +} + +type FilledRequests struct { + Proof SPVProof `json:"proof"` + Filled []FilledRequestInfo `json:"requests"` +} diff --git a/testVectors.json b/testVectors.json index ccf59b02..ad44da33 100644 --- a/testVectors.json +++ b/testVectors.json @@ -1196,6 +1196,150 @@ "output": "" }] }, + "validator": { + "validateProof": [{ + "proof": { + "version": "0x01000000", + "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "locktime": "0x00000000", + "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", + "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "index": 26, + "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d","confirming_header": { + "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", + "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592920, + "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", + "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", + "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", + "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + } + }, + "bestKnown": { + "hash_le": "0x5d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592924 + }, + "lca": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000", + "error": 0 + }, + { + "proof": { + "version": "0x01000000", + "vin": "0x0102748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "locktime": "0x00000000", + "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", + "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "index": 26, + "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d","confirming_header": { + "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", + "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592920, + "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", + "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", + "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", + "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + } + }, + "lca": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000", + "error": 108 + }], + "checkRequestsFilled": [{ + "filledRequest": { + "proof": { + "version": "0x01000000", + "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "locktime": "0x00000000", + "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", + "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "index": 26, + "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", + "confirming_header": { + "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", + "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592920, + "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", + "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", + "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", + "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + } + }, + "requests": [{ + "inputIndex": 0, + "outputIndex": 1, + "id": "0x0000000000000000" + }] + }, + "error": 0 + }, { + "filledRequest": { + "proof": { + "version": "0x01000000", + "vin": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "locktime": "0x00000000", + "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", + "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "index": 26, + "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", + "confirming_header": { + "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", + "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592920, + "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", + "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", + "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", + "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + } + }, + "requests": [{ + "inputIndex": 0, + "outputIndex": 1, + "id": "0x0000000000000000" + }] + }, + "error": 108 + }, { + "filledRequest": { + "proof": { + "version": "0x01000000", + "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", + "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", + "locktime": "0x00000000", + "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", + "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "index": 26, + "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", + "confirming_header": { + "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", + "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "height": 592920, + "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", + "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", + "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", + "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + } + }, + "requests": [{ + "inputIndex": 0, + "outputIndex": 1, + "id": "0x0000000000000000" + }, { + "inputIndex": 0, + "outputIndex": 1, + "id": "0x0700000000000000" + }] + }, + "error": 601 + }] + }, "requests": { "emptyRequest": { "spends": "0x1406e05881e299367766d313e26c05564ec91bf721d31726bd6e46e60689539a", @@ -1210,24 +1354,21 @@ "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "requestID": "0x0000000000000000", - "error": 605, - "output": false + "error": 605 }, { "inputIndex": 1, "outputIndex": 2, "vin": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffff", "vout": "0x02", "requestID": "0x0100000000000000", - "error": 604, - "output": false + "error": 604 }, { "inputIndex": 1, "outputIndex": 2, "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x004897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", "requestID": "0x0100000000000000", - "error": 605, - "output": false + "error": 605 }] } } From 7a4a18b3f8c445e9bf69d23f2e642def7c785db2 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Wed, 29 Jan 2020 14:53:49 -0700 Subject: [PATCH 056/148] Expose difficulty golang (#33) * started * added current and prev difficulty * added difficulty change setting in addHeadersWithRetarget * added difficulty check/setting in MarkNewHeaviest * deleted old comments * added difficulty change setting to ingestDifficultyChange * added updatePrevEpochDifficulty --- golang/x/relay/keeper/chain.go | 16 +++++++ golang/x/relay/keeper/headers.go | 62 +++++++++++++++++++++++++++ golang/x/relay/keeper/headers_test.go | 25 ++++++++++- golang/x/relay/keeper/keeper.go | 1 + golang/x/relay/keeper/keeper_test.go | 4 ++ golang/x/relay/types/keys.go | 8 ++++ 6 files changed, 115 insertions(+), 1 deletion(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 6fee1e4c..4bf5d607 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -172,6 +172,22 @@ func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, c return types.ErrNotHeavier(types.DefaultCodespace) } + // get newBestHeader + newBestHeader, getHeaderErr := k.GetHeader(ctx, newBestDigest) + if getHeaderErr != nil { + return getHeaderErr + } + // extract difficulty + newDiff := btcspv.ExtractDifficulty(newBestHeader.Raw) + // get currentEpochDifficulty + currentEpochDiff := k.getCurrentEpochDifficulty(ctx) + if newDiff != currentEpochDiff { + err := k.setCurrentEpochDifficulty(ctx, newDiff) + if err != nil { + return err + } + } + k.setLastReorgLCA(ctx, ancestor) k.setBestKnownDigest(ctx, newBestDigest) k.emitReorg(ctx, knownBestDigest, newBestDigest, ancestor) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 3b652b69..1f2edc50 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -37,6 +37,61 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) (types. return header, nil } +func (k Keeper) getCurrentEpochDifficulty(ctx sdk.Context) sdk.Uint { + store := k.getHeaderStore(ctx) + result := store.Get([]byte(types.CurrentEpochDiffStorage)) + + var diff sdk.Uint + diff.UnmarshalJSON(result) + + return diff +} + +func (k Keeper) setCurrentEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Error { + store := k.getHeaderStore(ctx) + + b, err := diff.MarshalJSON() + if err != nil { + return types.ErrExternal(types.DefaultCodespace, err) + } + + store.Set([]byte(types.CurrentEpochDiffStorage), b) + return nil +} + +func (k Keeper) getPrevEpochDifficulty(ctx sdk.Context) sdk.Uint { + store := k.getHeaderStore(ctx) + result := store.Get([]byte(types.PrevEpochDiffStorage)) + + var diff sdk.Uint + diff.UnmarshalJSON(result) + + return diff +} + +func (k Keeper) setPrevEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Error { + store := k.getHeaderStore(ctx) + + b, err := diff.MarshalJSON() + if err != nil { + return types.ErrExternal(types.DefaultCodespace, err) + } + + store.Set([]byte(types.PrevEpochDiffStorage), b) + return nil +} + +func (k Keeper) updatePrevEpochDifficulty(ctx sdk.Context, oldDiff sdk.Uint) sdk.Error { + prevEpochDiff := k.getPrevEpochDifficulty(ctx) + if prevEpochDiff != oldDiff { + err := k.setPrevEpochDifficulty(ctx, oldDiff) + if err != nil { + return err + } + } + return nil +} + // compareTargets compares Bitcoin truncated and full-length targets func compareTargets(full, truncated sdk.Uint) bool { // dirty hacks. sdk.Uint doesn't give us easy access to the underlying @@ -150,6 +205,7 @@ func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anc if !compareTargets(expectedTarget, actualTarget) { return types.ErrBadRetarget(types.DefaultCodespace) } + return nil } @@ -169,6 +225,12 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H return err } + oldDiff := btcspv.ExtractDifficulty(prevEpochStart.Raw) + err = k.updatePrevEpochDifficulty(ctx, oldDiff) + if err != nil { + return err + } + return k.ingestHeaders(ctx, headers, true) } diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index fcf7c97f..3ce3eb29 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -1,6 +1,9 @@ package keeper -import "github.com/cosmos/cosmos-sdk/types" +import ( + "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) func (s *KeeperSuite) TestGetHeader() { // errors if header is not found @@ -137,3 +140,23 @@ func (s *KeeperSuite) TestCompareTargets() { s.Equal(tc.Output, result) } } + +func (s *KeeperSuite) TestSetCurrentEpochDiff() { + val := sdk.NewUint(1000) + err := s.Keeper.setCurrentEpochDifficulty(s.Context, val) + s.SDKNil(err) + + d := s.Keeper.getCurrentEpochDifficulty(s.Context) + + s.Equal(d, val) +} + +func (s *KeeperSuite) TestSetPrevEpochDiff() { + val := sdk.NewUint(1000) + err := s.Keeper.setPrevEpochDifficulty(s.Context, val) + s.SDKNil(err) + + d := s.Keeper.getPrevEpochDifficulty(s.Context) + + s.Equal(d, val) +} diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 0f58f93f..c930af01 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -56,6 +56,7 @@ func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.Bitc k.setRelayGenesis(ctx, genesis.HashLE) k.setBestKnownDigest(ctx, genesis.HashLE) k.setLastReorgLCA(ctx, genesis.HashLE) + k.setCurrentEpochDifficulty(ctx, btcspv.ExtractDifficulty(genesis.Raw)) return nil } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index b794386b..22370515 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/suite" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -308,6 +309,9 @@ func (s *KeeperSuite) TestSetGenesisState() { s.SDKNil(err) s.Equal(genesis.HashLE, best) + diff := s.Keeper.getCurrentEpochDifficulty(s.Context) + s.Equal(btcspv.ExtractDifficulty(genesis.Raw), diff) + err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) s.Equal(types.AlreadyInit, err.Code()) } diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index a5e3a7dd..b393a6b1 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -28,5 +28,13 @@ const ( // LastReorgLCAStorage is the storage key for the last reorg LCA LastReorgLCAStorage = "LastReorgLCA" + // currentEpochDiffStorage is the storage key for the current epoch difficulty + CurrentEpochDiffStorage = "currentEpochDifficulty" + + // prevEpochDiffStorage is the storage key for the prev epoch difficulty + PrevEpochDiffStorage = "prevEpochDifficulty" + + // RequestIDTag is the storage key for the next Request ID to be used + // when storing a request RequestIDTag = "id" ) From d66f129e9e2a0603e5fee1960ea90c1689dc3f0f Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 29 Jan 2020 13:59:33 -0800 Subject: [PATCH 057/148] chore: swallow errors to make linter happy --- golang/x/relay/keeper/headers.go | 6 ++++-- golang/x/relay/keeper/keeper.go | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index 1f2edc50..f317798c 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -42,7 +42,8 @@ func (k Keeper) getCurrentEpochDifficulty(ctx sdk.Context) sdk.Uint { result := store.Get([]byte(types.CurrentEpochDiffStorage)) var diff sdk.Uint - diff.UnmarshalJSON(result) + // This will only fail if the store is corrupted + _ = diff.UnmarshalJSON(result) return diff } @@ -64,7 +65,8 @@ func (k Keeper) getPrevEpochDifficulty(ctx sdk.Context) sdk.Uint { result := store.Get([]byte(types.PrevEpochDiffStorage)) var diff sdk.Uint - diff.UnmarshalJSON(result) + // This will only fail if the store is corrupted + _ = diff.UnmarshalJSON(result) return diff } diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index c930af01..7e06c6c7 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -56,7 +56,9 @@ func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.Bitc k.setRelayGenesis(ctx, genesis.HashLE) k.setBestKnownDigest(ctx, genesis.HashLE) k.setLastReorgLCA(ctx, genesis.HashLE) - k.setCurrentEpochDifficulty(ctx, btcspv.ExtractDifficulty(genesis.Raw)) + + // this will only fail if the genesis state is corrupt + _ = k.setCurrentEpochDifficulty(ctx, btcspv.ExtractDifficulty(genesis.Raw)) return nil } From b1aa3fc8e3708abd14372de79e929d56ccaa0fda Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Fri, 13 Mar 2020 11:53:33 -0700 Subject: [PATCH 058/148] chore: update README --- golang/README.md | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/golang/README.md b/golang/README.md index 0153ce31..523d129b 100644 --- a/golang/README.md +++ b/golang/README.md @@ -1,11 +1,12 @@ ## TODOs: - [X] Milestone 1 -- [ ] Milestone 2 +- [X] Milestone 2 - - [X] Expose best-known digest - - [X] Expose LCA of reorg - - [X] Follow API of existing Solidity Relay -- - [ ] Validate SPV Proofs +- - [X] Validate SPV Proofs +- - [ ] `ProvideProof` message - [ ] Milestone 3 - - [ ] Provide hooks to execute tasks + dispatch messages - - [ ] Add a basic web dashboard with Relay health @@ -34,7 +35,7 @@ | CheckProof | TODO | Check the syntactic validity of an SPV Proof | | CheckRequests | TODO | Perform CheckProof and check the SPV Proof against a set of Requests | -## How to add a view function +## How to add a view function (queries) 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` 1. Add response type to `x/relay/types/querier.go` 1. Response type is a struct with the return values @@ -43,15 +44,28 @@ 1. Add new string tag for the new query 1. Add new `query___` function 1. Add new case block to `switch` in `NewQuerier()` ---- DO UP TO HERE FOR NOW --- 1. Add alias type in `x/relay/alias.go` 1. Add to CLI 1. add to `x/relay/client/cli/query.go` + 1. `func GetCmd______` + 1. returns a `cobra.Command` object + 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` + 1. `RunE` parses args, returns errors, and calls `cliCtx.QueryWithData` + 1. parses the output and returns it with `cliCtx.PrintOutput` 1. Add to REST 1. add to `x/relay/client/rest/query.go` + 1. new function `_____Handler` + 1. parse args and build structs + 1. cliCtx.QueryWithData + 1. return errors with `rest.WriteErrorResponse` + 1. return query result with `rest.PostProcessResponse` 1. add GET route to `x/relay/client/rest/rest.go` + 1. new `s.HandleFunc` with the route and arguments + 1. `.Methods("GET")` + 1. duplicate for optional args (see `isancestor` for example) -## How to add a non-view function + +## How to add a non-view function (messages) 1. Add necessary getters/setters in `x/relay/keeper/keeper.go` 1. Add msg type in `x/relay/types/msgs.go` 1. Message type is a struct with the arguments @@ -64,12 +78,23 @@ 1. Add to handler 1. Add new `handle____` function 1. Add new case block to `switch` in `NewHandler()` ---- DO UP TO HERE FOR NOW --- 1. Add aliases in `x/relay/alias.go` 1. Add alias in `var` block 1. Add alias in `type` block 1. Add to CLI 1. add to `x/relay/client/cli/tx.go` + 1. `func GetCmd______` + 1. returns a `cobra.Command` object + 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` + 1. `RunE` parses args, returns errors, and calls `utils.GenerateOrBroadcastMsgs` 1. Add to REST 1. add to `x/relay/client/rest/tx.go` + 1. new http request struct `______Req` + 1. `BaseReq` + the struct from `x/relay/types/msgs.go` + 1. new function `_____Handler` + 1. parse args and build structs + 1. return errors with `rest.WriteErrorResponse` + 1. make the tx with `utils.WriteGenerateStdTxResponse` 1. add POST route to `x/relay/client/rest/rest.go` + 1. new `s.HandleFunc` with the route and arguments + 1. `.Methods("POST")` From 7c9923ced4e4d118b0b59470697be39bbc09a30b Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Fri, 13 Mar 2020 12:37:29 -0700 Subject: [PATCH 059/148] dependency: bump version of btcspv, refactor for new API (#35) --- golang/go.mod | 2 +- golang/go.sum | 2 ++ golang/x/relay/client/cli/tx.go | 1 - golang/x/relay/keeper/keeper_test.go | 4 ++-- golang/x/relay/keeper/request_test.go | 5 +++-- golang/x/relay/keeper/requests.go | 9 ++++++--- golang/x/relay/types/validator.go | 6 +++--- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index 59651c0f..1f6fe35b 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -20,7 +20,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.5.0 github.com/stretchr/testify v1.4.0 - github.com/summa-tx/bitcoin-spv/golang v1.2.0 + github.com/summa-tx/bitcoin-spv/golang v1.3.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 diff --git a/golang/go.sum b/golang/go.sum index c58ec2ba..707a8dbb 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -367,6 +367,8 @@ github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07 h1:5 github.com/summa-tx/bitcoin-spv/golang v1.1.1-0.20191113003027-995ff9fb2d07/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/summa-tx/bitcoin-spv/golang v1.2.0 h1:d9VSHSb4HMQOYshbOTRh26g5yU4tUiBIn4cr9hlB2CE= github.com/summa-tx/bitcoin-spv/golang v1.2.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.3.0 h1:BHs+LZsCYd/UhHssiGuj6TZ3h8eBytIOVtiBTfVOiLc= +github.com/summa-tx/bitcoin-spv/golang v1.3.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index b6cfc543..5a997dbe 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -197,7 +197,6 @@ func GetCmdMarkNewHeaviest(cdc *codec.Codec) *cobra.Command { } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) - }, } } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 22370515..b11e8d98 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -164,8 +164,8 @@ type ValidatorTestCases struct { /***** Request TEST CASES *****/ type CheckRequestTestCase struct { - InputIdx uint8 `json:"inputIndex"` - OutputIdx uint8 `json:"outputIndex"` + InputIdx uint32 `json:"inputIndex"` + OutputIdx uint32 `json:"outputIndex"` Vin types.HexBytes `json:"vin"` Vout types.HexBytes `json:"vout"` RequestID types.RequestID `json:"requestID"` diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go index 1eafb4aa..ffbbf9db 100644 --- a/golang/x/relay/keeper/request_test.go +++ b/golang/x/relay/keeper/request_test.go @@ -131,7 +131,7 @@ func (s *KeeperSuite) TestCheckRequests() { s.Equal(sdk.CodeType(607), err.Code()) // Errors if output value is less than pays value - out, outErr := btcspv.ExtractOutputAtIndex(v.Vout, v.OutputIdx) + out, outErr := btcspv.ExtractOutputAtIndex(v.Vout, uint(v.OutputIdx)) s.Nil(outErr) // out[8:] extracts the output script which we use to set the request requestErr = s.Keeper.setRequest(s.Context, []byte{0}, out[8:], 1000, 0) @@ -158,7 +158,8 @@ func (s *KeeperSuite) TestCheckRequests() { s.Equal(sdk.CodeType(609), err.Code()) // Success - in := btcspv.ExtractInputAtIndex(v.Vin, v.InputIdx) + in, extractErr := btcspv.ExtractInputAtIndex(v.Vin, uint(v.InputIdx)) + s.Nil(extractErr) outpoint := btcspv.ExtractOutpoint(in) // out[8:] extracts the output script which we use to set the request requestErr = s.Keeper.setRequest(s.Context, outpoint, out[8:], 10, 255) diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 0b3c0e7a..28d59364 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -129,7 +129,7 @@ func (k Keeper) getNextID(ctx sdk.Context) (types.RequestID, sdk.Error) { return newID, nil } -func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vin []byte, vout []byte, requestID types.RequestID) sdk.Error { +func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, vin []byte, vout []byte, requestID types.RequestID) sdk.Error { if !btcspv.ValidateVin(vin) { return types.ErrInvalidVin(types.DefaultCodespace) } @@ -148,7 +148,7 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vi hasPays := req.Pays != btcspv.Hash256([]byte{0}) if hasPays { // We can ignore this error because we know that ValidateVout passed - out, _ := btcspv.ExtractOutputAtIndex(vout, outputIndex) + out, _ := btcspv.ExtractOutputAtIndex(vout, uint(outputIndex)) // hash the output script (out[8:]) outDigest := btcspv.Hash256(out[8:]) if outDigest != req.Pays { @@ -162,7 +162,10 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint8, vi hasSpends := req.Spends != btcspv.Hash256([]byte{0}) if hasSpends { - in := btcspv.ExtractInputAtIndex(vin, inputIndex) + in, err := btcspv.ExtractInputAtIndex(vin, uint(inputIndex)) + if err != nil { + return types.FromBTCSPVError(types.DefaultCodespace, err) + } outpoint := btcspv.ExtractOutpoint(in) inDigest := btcspv.Hash256(outpoint) if hasSpends && inDigest != req.Spends { diff --git a/golang/x/relay/types/validator.go b/golang/x/relay/types/validator.go index 22e189ff..d4d56f0a 100644 --- a/golang/x/relay/types/validator.go +++ b/golang/x/relay/types/validator.go @@ -1,9 +1,9 @@ package types type FilledRequestInfo struct { - InputIndex uint8 `json:"inputIndex"` - OutputIndex uint8 `json:"outputIndex"` - ID RequestID `json:"id"` + InputIndex uint32 `json:"inputIndex"` + OutputIndex uint32 `json:"outputIndex"` + ID RequestID `json:"id"` } type FilledRequests struct { From ab9ac962ba42c2a1105910acf111eba7942d1f7c Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Fri, 13 Mar 2020 19:07:54 -0400 Subject: [PATCH 060/148] plumbing: ProvideProof functionality Add CLI entrypoint for ProvideProof REST endpoint for ProvideProof validate ProvideProof's FilledRequests Add REST route --- golang/x/relay/alias.go | 2 ++ golang/x/relay/client/cli/tx.go | 44 ++++++++++++++++++++++++++++ golang/x/relay/client/rest/rest.go | 1 + golang/x/relay/client/rest/tx.go | 40 +++++++++++++++++++++++++ golang/x/relay/keeper/handler.go | 14 +++++++++ golang/x/relay/keeper/keeper_test.go | 4 +-- golang/x/relay/types/msgs.go | 36 +++++++++++++++++++++++ golang/x/relay/types/validator.go | 13 ++++++-- 8 files changed, 149 insertions(+), 5 deletions(-) diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index 19558db9..255f16f4 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -27,6 +27,8 @@ var ( NewMsgMarkNewHeaviest = types.NewMsgMarkNewHeaviest // NewMsgGetRequest is what is says on the tin NewMsgNewRequest = types.NewMsgNewRequest + // NewMsgProvideProof is what is says on the tin + NewMsgProvideProof = types.NewMsgProvideProof // RegisterCodec is what is says on the tin RegisterCodec = types.RegisterCodec // ModuleCdc is what is says on the tin diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 5a997dbe..90bba4ce 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -150,6 +150,50 @@ func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { } } +// GetCmdProvideProof stores a new proof request +func GetCmdProvideProof(cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "provideproof ", + Short: "validates proof of given requests", + Long: "validates proof of given requests", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + + var proof types.SPVProof + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } + + var requests []types.FilledRequestInfo + jsonErr = json.Unmarshal([]byte(args[1]), &requests) + if jsonErr != nil { + return jsonErr + } + + filledRequests := types.NewFilledRequests( + proof, + requests, + ) + + msg := types.NewMsgProvideProof( + cliCtx.GetFromAddress(), + filledRequests, + ) + + err := msg.ValidateBasic() + if err != nil { + return err + } + + return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) + + }, + } +} + // GetCmdMarkNewHeaviest creates a CLI command to update best known digest and LCA func GetCmdMarkNewHeaviest(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index e3603cf0..f80a058b 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -17,6 +17,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) s.HandleFunc("/ingestdiffchange", ingestDifficultyChangeHandler(cliCtx)).Methods("POST") s.HandleFunc("/marknewheaviest", markNewHeaviestHandler(cliCtx)).Methods("POST") s.HandleFunc("/newrequest", newRequestHandler(cliCtx)).Methods("POST") + s.HandleFunc("/provideproof", provideProofHandler(cliCtx)).Methods("POST") // add new query routes below // {} denotes variable parts of the url route diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 150cdacf..2e6ab850 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -165,3 +165,43 @@ func newRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) } } + +type ProvideProofReq struct { + BaseReq rest.BaseReq `json:"base_req"` + Proof types.SPVProof `json:"proof"` + Requests []types.FilledRequestInfo `json:"filled_requests"` + Sender string `json:"sender"` +} + +func provideProofHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req ProvideProofReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w) { + return + } + + addr, err := sdk.AccAddressFromBech32(req.Sender) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + filledRequests := types.NewFilledRequests(req.Proof, req.Requests) + + msg := types.NewMsgProvideProof(addr, filledRequests) + err = msg.ValidateBasic() + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + utils.WriteGenerateStdTxResponse(w, cliCtx, baseReq, []sdk.Msg{msg}) + } +} diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 6a573a54..1d80877a 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -19,6 +19,8 @@ func NewHandler(keeper Keeper) sdk.Handler { return handleMsgMarkNewHeaviest(ctx, keeper, msg) case types.MsgNewRequest: return handleMsgNewRequest(ctx, keeper, msg) + case types.MsgProvideProof: + return handleMsgProvideProof(ctx, keeper, msg) default: errMsg := fmt.Sprintf("Unrecognized relay Msg type: %v", msg.Type()) return sdk.ErrUnknownRequest(errMsg).Result() @@ -74,3 +76,15 @@ func handleMsgNewRequest(ctx sdk.Context, keeper Keeper, msg types.MsgNewRequest Events: ctx.EventManager().Events(), } } + +func handleMsgProvideProof(ctx sdk.Context, keeper Keeper, msg types.MsgProvideProof) sdk.Result { + err := keeper.checkRequestsFilled(ctx, msg.Filled) + if err != nil { + return err.Result() + } + + // TODO: Add "hooks" + return sdk.Result{ + Events: ctx.EventManager().Events(), + } +} diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index b11e8d98..3f74fa79 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -164,8 +164,8 @@ type ValidatorTestCases struct { /***** Request TEST CASES *****/ type CheckRequestTestCase struct { - InputIdx uint32 `json:"inputIndex"` - OutputIdx uint32 `json:"outputIndex"` + InputIdx uint32 `json:"inputIndex"` + OutputIdx uint32 `json:"outputIndex"` Vin types.HexBytes `json:"vin"` Vout types.HexBytes `json:"vout"` RequestID types.RequestID `json:"requestID"` diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index fb11c89b..e77df753 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -167,3 +167,39 @@ func (msg MsgNewRequest) GetSignBytes() []byte { } func (msg MsgNewRequest) Route() string { return RouterKey } + +/***** ProvideProof *****/ + +// MsgProvideProof defines a NewRequest message +type MsgProvideProof struct { + Signer sdk.AccAddress `json:"signer"` + Filled FilledRequests `json:"filled"` +} + +func NewMsgProvideProof(address sdk.AccAddress, filledRequests FilledRequests) MsgProvideProof { + return MsgProvideProof{ + address, + filledRequests, + } +} + +func (msg MsgProvideProof) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Signer} +} + +func (msg MsgProvideProof) ValidateBasic() sdk.Error { + valid, err := msg.Filled.Proof.Validate() + if !valid || err != nil { + return FromBTCSPVError(DefaultCodespace, err) + } + + return nil +} + +func (msg MsgProvideProof) Type() string { return "provide_proof" } + +func (msg MsgProvideProof) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgProvideProof) Route() string { return RouterKey } diff --git a/golang/x/relay/types/validator.go b/golang/x/relay/types/validator.go index d4d56f0a..939b2e3b 100644 --- a/golang/x/relay/types/validator.go +++ b/golang/x/relay/types/validator.go @@ -1,12 +1,19 @@ package types type FilledRequestInfo struct { - InputIndex uint32 `json:"inputIndex"` - OutputIndex uint32 `json:"outputIndex"` - ID RequestID `json:"id"` + InputIndex uint32 `json:"inputIndex"` + OutputIndex uint32 `json:"outputIndex"` + ID RequestID `json:"id"` } type FilledRequests struct { Proof SPVProof `json:"proof"` Filled []FilledRequestInfo `json:"requests"` } + +func NewFilledRequests(proof SPVProof, filled []FilledRequestInfo) FilledRequests { + return FilledRequests{ + proof, + filled, + } +} From 8cfba5c4fe0bf633c5c196e59bd30a1a6ad30024 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 16 Mar 2020 14:07:16 -0700 Subject: [PATCH 061/148] chore: update README and comments --- golang/README.md | 4 ++-- golang/x/relay/types/querier.go | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/golang/README.md b/golang/README.md index 523d129b..188915aa 100644 --- a/golang/README.md +++ b/golang/README.md @@ -6,7 +6,7 @@ - - [X] Expose LCA of reorg - - [X] Follow API of existing Solidity Relay - - [X] Validate SPV Proofs -- - [ ] `ProvideProof` message +- - [X] `ProvideProof` message - [ ] Milestone 3 - - [ ] Provide hooks to execute tasks + dispatch messages - - [ ] Add a basic web dashboard with Relay health @@ -19,7 +19,7 @@ | IngestDifficultyChange | Completed | Add a chain of headers to the relay with a difficulty change| | MarkNewHeaviest | Completed | Mark a new best-known chain tip | | NewRequest | Completed | Register a new SPV Proof request| -| ProvideProof | TODO | Provide a proof that satisfies 1 or more requests | +| ProvideProof | Completed | Provide a proof that satisfies 1 or more requests | ## Queries added to CLI diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index f2dfd2e5..68e7557a 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -97,6 +97,7 @@ func (r QueryResFindAncestor) String() string { dig, offset, res) } +// QueryParamsHeaviestFromAncestor is the params struct for queryHeaviestFromAncestor type QueryParamsHeaviestFromAncestor struct { Ancestor Hash256Digest `json:"ancestor"` CurrentBest Hash256Digest `json:"currentBest"` @@ -121,6 +122,7 @@ func (r QueryResHeaviestFromAncestor) String() string { anc, curBest, newBest, r.Params.Limit, res) } +// QueryParamsIsMostRecentCommonAncestor is the params struct for queryIsMostRecentCommonAncestor type QueryParamsIsMostRecentCommonAncestor struct { Ancestor Hash256Digest `json:"ancestor"` Left Hash256Digest `json:"left"` @@ -144,11 +146,12 @@ func (r QueryResIsMostRecentCommonAncestor) String() string { anc, left, right, r.Params.Limit, r.Res) } +// QueryParamsGetRequest is the response struct for queryGetRequest type QueryParamsGetRequest struct { ID RequestID `json:"id"` } -// QueryResIsMostRecentCommonAncestor is the response struct for queryIsMostRecentCommonAncestor +// QueryResGetRequest is the response struct for queryGetRequest type QueryResGetRequest struct { Params QueryParamsGetRequest `json:"params"` Res ProofRequest `json:"result"` From 52ffd69e8b6e9a81c54d0342a96a90f7acbe26d6 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 16 Mar 2020 14:13:15 -0700 Subject: [PATCH 062/148] chore: add export comments --- golang/x/relay/types/errors.go | 4 +++- golang/x/relay/types/events.go | 2 +- golang/x/relay/types/keys.go | 4 ++-- golang/x/relay/types/msgs.go | 30 ++++++++++++++++++++++++++++++ golang/x/relay/types/validator.go | 3 +++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 0882c59c..2eaf61bd 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -115,6 +115,7 @@ const ( // 500-block Queries // TODO: Delete the next 2 error codes + // NotEnoughArguments means there are not enough arguments specified in the path of a query NotEnoughArguments sdk.CodeType = 501 // NotEnoughArgumentsMessage is the corresponding message @@ -184,11 +185,12 @@ const ( // NotEnoughConfs means the proof does not have enough confirmations NotEnoughConfs sdk.CodeType = 611 - // NotEnoughConfs is the corresponding message + // NotEnoughConfsMessage is the corresponding message NotEnoughConfsMessage = "Not enough confirmations" // 700-block External + // ExternalError is an error from a dependency ExternalError sdk.CodeType = 701 ) diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 48ad3a2c..0ac2ed6a 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -45,7 +45,7 @@ func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { ) } -// NewExtensionEvent instantiates a proof request event +// NewProofRequestEvent instantiates a proof request event func NewProofRequestEvent(pays, spends []byte, paysValue uint64, id RequestID) sdk.Event { return sdk.NewEvent( EventTypeProofRequest, diff --git a/golang/x/relay/types/keys.go b/golang/x/relay/types/keys.go index b393a6b1..c9925b85 100644 --- a/golang/x/relay/types/keys.go +++ b/golang/x/relay/types/keys.go @@ -28,10 +28,10 @@ const ( // LastReorgLCAStorage is the storage key for the last reorg LCA LastReorgLCAStorage = "LastReorgLCA" - // currentEpochDiffStorage is the storage key for the current epoch difficulty + // CurrentEpochDiffStorage is the storage key for the current epoch difficulty CurrentEpochDiffStorage = "currentEpochDifficulty" - // prevEpochDiffStorage is the storage key for the prev epoch difficulty + // PrevEpochDiffStorage is the storage key for the prev epoch difficulty PrevEpochDiffStorage = "prevEpochDifficulty" // RequestIDTag is the storage key for the next Request ID to be used diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index e77df753..e20f7c62 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -15,6 +15,7 @@ type MsgIngestHeaderChain struct { Headers []BitcoinHeader `json:"headers"` } +// NewMsgIngestHeaderChain instantiates a MsgIngestHeaderChain func NewMsgIngestHeaderChain(address sdk.AccAddress, headers []BitcoinHeader) MsgIngestHeaderChain { return MsgIngestHeaderChain{ address, @@ -22,12 +23,15 @@ func NewMsgIngestHeaderChain(address sdk.AccAddress, headers []BitcoinHeader) Ms } } +// GetSigners gets signers func (msg MsgIngestHeaderChain) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +// Type returns an identifier func (msg MsgIngestHeaderChain) Type() string { return "ingest_header_chain" } +// ValidateBasic runs stateless validation func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() @@ -38,10 +42,12 @@ func (msg MsgIngestHeaderChain) ValidateBasic() sdk.Error { return nil } +// GetSignBytes returns the sighash for the message func (msg MsgIngestHeaderChain) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +// Route returns the route key func (msg MsgIngestHeaderChain) Route() string { return RouterKey } /***** IngestDifficultyChange *****/ @@ -53,6 +59,7 @@ type MsgIngestDifficultyChange struct { Headers []BitcoinHeader `json:"headers"` } +// NewMsgIngestDifficultyChange instantiates a MsgIngestDifficultyChange func NewMsgIngestDifficultyChange(address sdk.AccAddress, start Hash256Digest, headers []BitcoinHeader) MsgIngestDifficultyChange { return MsgIngestDifficultyChange{ address, @@ -61,12 +68,15 @@ func NewMsgIngestDifficultyChange(address sdk.AccAddress, start Hash256Digest, h } } +// GetSigners gets signers func (msg MsgIngestDifficultyChange) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +// Type returns an identifier func (msg MsgIngestDifficultyChange) Type() string { return "ingest_difficulty_change" } +// ValidateBasic runs stateless validation func (msg MsgIngestDifficultyChange) ValidateBasic() sdk.Error { for i := range msg.Headers { valid, err := msg.Headers[i].Validate() @@ -77,10 +87,12 @@ func (msg MsgIngestDifficultyChange) ValidateBasic() sdk.Error { return nil } +// GetSignBytes returns the sighash for the message func (msg MsgIngestDifficultyChange) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +// Route returns the route key func (msg MsgIngestDifficultyChange) Route() string { return RouterKey } /***** MarkNewHeaviest *****/ @@ -94,6 +106,7 @@ type MsgMarkNewHeaviest struct { Limit uint32 `json:"limit"` } +// NewMsgMarkNewHeaviest instantiates a MsgMarkNewHeaviest func NewMsgMarkNewHeaviest(address sdk.AccAddress, ancestor Hash256Digest, currentBest RawHeader, newBest RawHeader, limit uint32) MsgMarkNewHeaviest { return MsgMarkNewHeaviest{ address, @@ -104,12 +117,15 @@ func NewMsgMarkNewHeaviest(address sdk.AccAddress, ancestor Hash256Digest, curre } } +// GetSigners gets signers func (msg MsgMarkNewHeaviest) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +// Type returns an identifier func (msg MsgMarkNewHeaviest) Type() string { return "mark_new_heaviest" } +// ValidateBasic runs stateless validation func (msg MsgMarkNewHeaviest) ValidateBasic() sdk.Error { if len(msg.CurrentBest) != 80 || len(msg.NewBest) != 80 { return ErrBadHeaderLength(DefaultCodespace) @@ -118,10 +134,12 @@ func (msg MsgMarkNewHeaviest) ValidateBasic() sdk.Error { return nil } +// GetSignBytes returns the sighash for the message func (msg MsgMarkNewHeaviest) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +// Route returns the route key func (msg MsgMarkNewHeaviest) Route() string { return RouterKey } /***** NewRequest *****/ @@ -135,6 +153,7 @@ type MsgNewRequest struct { NumConfs uint8 `json:"numConfs"` } +// NewMsgNewRequest instantiates a MsgNewRequest func NewMsgNewRequest(address sdk.AccAddress, spends, pays []byte, paysValue uint64, numConfs uint8) MsgNewRequest { return MsgNewRequest{ address, @@ -145,12 +164,15 @@ func NewMsgNewRequest(address sdk.AccAddress, spends, pays []byte, paysValue uin } } +// GetSigners gets signers func (msg MsgNewRequest) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +// Type returns an identifier func (msg MsgNewRequest) Type() string { return "new_request" } +// ValidateBasic runs stateless validation func (msg MsgNewRequest) ValidateBasic() sdk.Error { // TODO: validate output types if len(msg.Spends) != 36 { @@ -162,10 +184,12 @@ func (msg MsgNewRequest) ValidateBasic() sdk.Error { return nil } +// GetSignBytes returns the sighash for the message func (msg MsgNewRequest) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +// Route returns the route key func (msg MsgNewRequest) Route() string { return RouterKey } /***** ProvideProof *****/ @@ -176,6 +200,7 @@ type MsgProvideProof struct { Filled FilledRequests `json:"filled"` } +// NewMsgProvideProof instantiates a MsgProvideProof func NewMsgProvideProof(address sdk.AccAddress, filledRequests FilledRequests) MsgProvideProof { return MsgProvideProof{ address, @@ -183,10 +208,12 @@ func NewMsgProvideProof(address sdk.AccAddress, filledRequests FilledRequests) M } } +// GetSigners gets signers func (msg MsgProvideProof) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.Signer} } +// ValidateBasic runs stateless validation func (msg MsgProvideProof) ValidateBasic() sdk.Error { valid, err := msg.Filled.Proof.Validate() if !valid || err != nil { @@ -196,10 +223,13 @@ func (msg MsgProvideProof) ValidateBasic() sdk.Error { return nil } +// Type returns an identifier func (msg MsgProvideProof) Type() string { return "provide_proof" } +// GetSignBytes returns the sighash for the message func (msg MsgProvideProof) GetSignBytes() []byte { return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) } +// Route returns the route key func (msg MsgProvideProof) Route() string { return RouterKey } diff --git a/golang/x/relay/types/validator.go b/golang/x/relay/types/validator.go index 939b2e3b..d4cf2e03 100644 --- a/golang/x/relay/types/validator.go +++ b/golang/x/relay/types/validator.go @@ -1,16 +1,19 @@ package types +// FilledRequestInfo contains information about what input and/or output satisfied the request type FilledRequestInfo struct { InputIndex uint32 `json:"inputIndex"` OutputIndex uint32 `json:"outputIndex"` ID RequestID `json:"id"` } +// FilledRequests contains a proof that satisfies one or more requests type FilledRequests struct { Proof SPVProof `json:"proof"` Filled []FilledRequestInfo `json:"requests"` } +// NewFilledRequests instantiates a FilledRequests func NewFilledRequests(proof SPVProof, filled []FilledRequestInfo) FilledRequests { return FilledRequests{ proof, From fc9a369839cfc5495ae2c579de294e1fab7fd311 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Tue, 17 Mar 2020 10:47:50 -0400 Subject: [PATCH 063/148] plumbing: checkrequests query functionality Return full QueryResCheckRequests when validation is false Process REST checkRequests as POST and drop String parsing attempts --- golang/x/relay/client/cli/query.go | 51 +++++++++++++++++++++++++++++ golang/x/relay/client/rest/query.go | 44 ++++++++++++++++++++++++- golang/x/relay/client/rest/rest.go | 1 + golang/x/relay/keeper/querier.go | 34 +++++++++++++++++++ golang/x/relay/types/errors.go | 2 +- golang/x/relay/types/msgs.go | 2 +- golang/x/relay/types/querier.go | 19 +++++++++++ 7 files changed, 150 insertions(+), 3 deletions(-) diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index b016823f..e8368be4 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -1,6 +1,7 @@ package cli import ( + "encoding/json" "fmt" "strconv" @@ -357,3 +358,53 @@ func GetCmdGetRequest(queryRoute string, cdc *codec.Codec) *cobra.Command { }, } } + +// GetCmdCheckRequest returns the CLI command struct for checkRequests +func GetCmdCheckRequests(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "checkrequests ", + Long: "check whether proof successfully validates a set of requests", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + var proof types.SPVProof + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } + + var requests []types.FilledRequestInfo + jsonErr = json.Unmarshal([]byte(args[1]), &requests) + if jsonErr != nil { + return jsonErr + } + + filledRequests := types.NewFilledRequests( + proof, + requests, + ) + + params := types.QueryParamsCheckRequests{ + Filled: filledRequests, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/checkrequests", queryData) + + if err != nil { + fmt.Printf("error processing checkrequests: %s \n", err) + return nil + } + + var out types.QueryResCheckRequests + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index 22cc57cf..398f865b 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -258,7 +258,7 @@ func heaviestFromAncestorHandler(cliCtx context.CLIContext, storeName string) ht } // handler function for getRequest queries. parses arguments from url string, and passes them through -// as a QueryParamsGetReqiest struct +// as a QueryParamsGetRequest struct func getRequestHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -289,3 +289,45 @@ func getRequestHandler(cliCtx context.CLIContext, storeName string) http.Handler rest.PostProcessResponse(w, cliCtx, res) } } + +// struct to help parse json parameters since checkRequests has params more complex +// than other view functions and hence technically comes in as a POST request +type checkRequestsReq struct { + Proof types.SPVProof `json:"proof"` + Requests []types.FilledRequestInfo `json:"filled_requests"` +} + +// handler function for checkRequests queries. parses arguments from url string, and passes them through +// as a QueryParamsCheckRequests struct +// Comes in as POST request will proceed to treat it as a GET +func checkRequestsHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req checkRequestsReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + filledRequests := types.NewFilledRequests(req.Proof, req.Requests) + + params := types.QueryParamsCheckRequests{ + Filled: filledRequests, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, _, err := cliCtx.QueryWithData("custom/relay/checkRequests", queryData) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index f80a058b..5d08de18 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -32,4 +32,5 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/{limit}", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/getrequest/{id}", getRequestHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/checkrequests", checkRequestsHandler(cliCtx, storeName)).Methods("POST") // technically a view only query, POST is due to complex params } diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index 3c28acbc..fbe6e2fa 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -40,6 +40,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryIsMostRecentCommonAncestor(ctx, req, keeper) case types.QueryGetRequest: return queryGetRequest(ctx, req, keeper) + case types.QueryCheckRequests: + return queryCheckRequests(ctx, req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } @@ -234,3 +236,35 @@ func queryGetRequest(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res } return res, nil } + +func queryCheckRequests(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsCheckRequests + var errMsg string + valid := true + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) + } + + // This calls the keeper with the parsed arguments, and gets an answer + resErr := keeper.checkRequestsFilled(ctx, params.Filled) + if resErr != nil { + valid = false + errMsg = resErr.Error() + } + + // Now we format the answer as a response + response := types.QueryResCheckRequests{ + Params: params, + Valid: valid, + ErrorMessage: errMsg, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 2eaf61bd..17c22e9e 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -115,7 +115,7 @@ const ( // 500-block Queries // TODO: Delete the next 2 error codes - + // NotEnoughArguments means there are not enough arguments specified in the path of a query NotEnoughArguments sdk.CodeType = 501 // NotEnoughArgumentsMessage is the corresponding message diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index e20f7c62..dd97437a 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -200,7 +200,7 @@ type MsgProvideProof struct { Filled FilledRequests `json:"filled"` } -// NewMsgProvideProof instantiates a MsgProvideProof +// NewMsgProvideProof instantiates a MsgProvideProof func NewMsgProvideProof(address sdk.AccAddress, filledRequests FilledRequests) MsgProvideProof { return MsgProvideProof{ address, diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 68e7557a..779bee69 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -2,6 +2,7 @@ package types import ( "encoding/hex" + "encoding/json" "fmt" ) @@ -29,6 +30,9 @@ const ( // QueryGetRequest is a query string tag for getRequest QueryGetRequest = "getrequest" + + // QueryCheckRequest is a query string tag for checkRequests + QueryCheckRequests = "checkrequests" ) // QueryParamsIsAncestor represents the parameters for an IsAncestor query @@ -165,3 +169,18 @@ func (r QueryResGetRequest) String() string { "ID: %d, Spends: %s, Pays: %s, Value: %d, Active: %t, Confirmations: %d", r.Params.ID, spends, pays, r.Res.PaysValue, r.Res.ActiveState, r.Res.NumConfs) } + +type QueryParamsCheckRequests struct { + Filled FilledRequests `json:"filledRequests"` +} + +type QueryResCheckRequests struct { + Params QueryParamsCheckRequests `json:"params"` + Valid bool `json:"valid"` + ErrorMessage string `json:"errorMessage"` +} + +func (r QueryResCheckRequests) String() string { + json, _ := json.Marshal(r) + return fmt.Sprint(string(json)) +} From a0889e6e9f707930315216f290d8586aa1e58148 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Thu, 19 Mar 2020 10:28:20 -0400 Subject: [PATCH 064/148] plumbing: checkProof functionality --- golang/x/relay/client/cli/query.go | 39 ++++++++++++++++++++++++++ golang/x/relay/client/rest/query.go | 43 +++++++++++++++++++++++++++-- golang/x/relay/client/rest/rest.go | 1 + golang/x/relay/keeper/querier.go | 34 +++++++++++++++++++++++ golang/x/relay/types/querier.go | 26 ++++++++++++++++- 5 files changed, 140 insertions(+), 3 deletions(-) diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index e8368be4..1b78a407 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -408,3 +408,42 @@ func GetCmdCheckRequests(queryRoute string, cdc *codec.Codec) *cobra.Command { }, } } + +// GetCmdCheckProof returns the CLI command struct for checkProof +func GetCmdCheckProof(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "checkproof ", + Long: "check proof has valid parameters", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + var proof types.SPVProof + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } + + params := types.QueryParamsCheckProof{ + Proof: proof, + } + + queryData, err := cdc.MarshalJSON(params) + if err != nil { + fmt.Print(err.Error()) + return nil + } + + res, _, err := cliCtx.QueryWithData("custom/relay/checkproof", queryData) + + if err != nil { + fmt.Printf("error processing checkproof: %s \n", err) + return nil + } + + var out types.QueryResCheckProof + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index 398f865b..9ebfd72f 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -290,8 +290,8 @@ func getRequestHandler(cliCtx context.CLIContext, storeName string) http.Handler } } -// struct to help parse json parameters since checkRequests has params more complex -// than other view functions and hence technically comes in as a POST request +// struct to help parse json parameters since checkRequests has params more complex than +// other view functions and hence technically comes in as a POST request w/ json params type checkRequestsReq struct { Proof types.SPVProof `json:"proof"` Requests []types.FilledRequestInfo `json:"filled_requests"` @@ -331,3 +331,42 @@ func checkRequestsHandler(cliCtx context.CLIContext, storeName string) http.Hand rest.PostProcessResponse(w, cliCtx, res) } } + +// struct to help parse json parameters since checkProof has params more complex than +// other view functions and hence technically comes in as a POST request w/ json +type checkProofReq struct { + Proof types.SPVProof `json:"proof"` +} + +// handler function for checkProof queries. parses arguments from url string, and passes them through +// as a QueryParamsCheckProof struct +// Comes in as POST request will proceed to treat it as a GET +func checkProofHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req checkProofReq + + if !rest.ReadRESTReq(w, r, cliCtx.Codec, &req) { + rest.WriteErrorResponse(w, http.StatusBadRequest, "failed to parse request") + return + } + + params := types.QueryParamsCheckProof{ + Proof: req.Proof, + } + + queryData, err := json.Marshal(params) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + res, _, err := cliCtx.QueryWithData("custom/relay/checkProof", queryData) + + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index 5d08de18..3d3e0e45 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -33,4 +33,5 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) s.HandleFunc("/heaviestfromancestor/{ancestor}/{currentbest}/{newbest}/{limit}", heaviestFromAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/getrequest/{id}", getRequestHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/checkrequests", checkRequestsHandler(cliCtx, storeName)).Methods("POST") // technically a view only query, POST is due to complex params + s.HandleFunc("/checkproof", checkProofHandler(cliCtx, storeName)).Methods("POST") // technically a view only query, POST is due to complex params } diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index fbe6e2fa..b6aefffe 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -42,6 +42,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryGetRequest(ctx, req, keeper) case types.QueryCheckRequests: return queryCheckRequests(ctx, req, keeper) + case types.QueryCheckProof: + return queryCheckProof(ctx, req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown relay query endpoint") } @@ -268,3 +270,35 @@ func queryCheckRequests(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ( } return res, nil } + +func queryCheckProof(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params types.QueryParamsCheckProof + var errMsg string + valid := true + + unmarshallErr := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms) + if unmarshallErr != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", unmarshallErr)) + } + + // This calls the keeper with the parsed arguments, and gets an answer + resErr := keeper.validateProof(ctx, params.Proof) + if resErr != nil { + valid = false + errMsg = resErr.Error() + } + + // Now we format the answer as a response + response := types.QueryResCheckProof{ + Params: params, + Valid: valid, + ErrorMessage: errMsg, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 779bee69..b2618676 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -33,6 +33,9 @@ const ( // QueryCheckRequest is a query string tag for checkRequests QueryCheckRequests = "checkrequests" + + // QueryCheckProof is a query string tag for checkProof + QueryCheckProof = "checkproof" ) // QueryParamsIsAncestor represents the parameters for an IsAncestor query @@ -170,17 +173,38 @@ func (r QueryResGetRequest) String() string { r.Params.ID, spends, pays, r.Res.PaysValue, r.Res.ActiveState, r.Res.NumConfs) } +// QueryParamsCheckRequests is the response struct for queryCheckRequests type QueryParamsCheckRequests struct { Filled FilledRequests `json:"filledRequests"` } +// QueryResCheckRequests is the response struct for queryCheckRequests type QueryResCheckRequests struct { Params QueryParamsCheckRequests `json:"params"` Valid bool `json:"valid"` ErrorMessage string `json:"errorMessage"` } +// String formats a QueryResCheckRequests struct func (r QueryResCheckRequests) String() string { json, _ := json.Marshal(r) - return fmt.Sprint(string(json)) + return string(json) +} + +// QueryParamsCheckProof is the response struct for queryCheckProof +type QueryParamsCheckProof struct { + Proof SPVProof `json:"proof"` +} + +// QueryResCheckProof is the response struct for queryCheckProof +type QueryResCheckProof struct { + Params QueryParamsCheckProof `json:"params"` + Valid bool `json:"valid"` + ErrorMessage string `json:"errorMessage"` +} + +// String formats a QueryResCheckProof struct +func (r QueryResCheckProof) String() string { + json, _ := json.Marshal(r) + return string(json) } From 87e2f17e43fec2909cbdd756ec995de7b20b56c1 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Fri, 20 Mar 2020 15:23:02 -0400 Subject: [PATCH 065/148] enhancement: (IBC) prep requests with Origin + Action for event dispatch (#40) * enhancement: (IBC) prep requests with Origin + Action for event dispatch * error msg for len(action) > 500 --- golang/x/relay/client/cli/tx.go | 2 ++ golang/x/relay/client/rest/tx.go | 2 +- golang/x/relay/keeper/handler.go | 2 +- golang/x/relay/keeper/handler_test.go | 6 +++--- golang/x/relay/keeper/querier_test.go | 2 +- golang/x/relay/keeper/request_test.go | 18 +++++++++--------- golang/x/relay/keeper/requests.go | 10 ++++++---- golang/x/relay/keeper/validator_test.go | 4 ++-- golang/x/relay/types/errors.go | 10 ++++++++++ golang/x/relay/types/events.go | 4 +++- golang/x/relay/types/msgs.go | 9 ++++++++- golang/x/relay/types/requests.go | 2 ++ golang/x/relay/types/types.go | 10 ++++++++++ 13 files changed, 58 insertions(+), 23 deletions(-) diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 90bba4ce..32a0abe3 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -138,6 +138,8 @@ func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { pays, paysValue, uint8(numConfs), + types.Local, + nil, ) err := msg.ValidateBasic() if err != nil { diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 2e6ab850..5db2774a 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -155,7 +155,7 @@ func newRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { return } - msg := types.NewMsgNewRequest(addr, req.Spends, req.Pays, req.PaysValue, req.NumConfs) + msg := types.NewMsgNewRequest(addr, req.Spends, req.Pays, req.PaysValue, req.NumConfs, types.Local, nil) err = msg.ValidateBasic() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 1d80877a..42164453 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -67,7 +67,7 @@ func handleMsgNewRequest(ctx sdk.Context, keeper Keeper, msg types.MsgNewRequest // TODO: Add more complex permissioning // Set request - err = keeper.setRequest(ctx, msg.Spends, msg.Pays, msg.PaysValue, msg.NumConfs) + err = keeper.setRequest(ctx, msg.Spends, msg.Pays, msg.PaysValue, msg.NumConfs, msg.Origin, msg.Action) if err != nil { return err.Result() } diff --git a/golang/x/relay/keeper/handler_test.go b/golang/x/relay/keeper/handler_test.go index f2c9bfcc..7fee1841 100644 --- a/golang/x/relay/keeper/handler_test.go +++ b/golang/x/relay/keeper/handler_test.go @@ -113,14 +113,14 @@ func (s *KeeperSuite) TestHandleNewRequest() { handler := NewHandler(s.Keeper) // Success - newRequest := types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0) + newRequest := types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0, types.Local, nil) res := handler(s.Context, newRequest) hasRequest := s.Keeper.hasRequest(s.Context, types.RequestID{}) s.Equal(true, hasRequest) s.Equal("proof_request", res.Events[0].Type) // Msg validation failed - newRequest = types.NewMsgNewRequest(getAccAddress(), []byte{0}, []byte{0}, 0, 0) + newRequest = types.NewMsgNewRequest(getAccAddress(), []byte{0}, []byte{0}, 0, 0, types.Local, nil) res = handler(s.Context, newRequest) s.Equal(sdk.CodeType(602), res.Code) @@ -128,7 +128,7 @@ func (s *KeeperSuite) TestHandleNewRequest() { store := s.Keeper.getRequestStore(s.Context) store.Set([]byte(types.RequestIDTag), []byte("badID")) - newRequest = types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0) + newRequest = types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0, types.Local, nil) res = handler(s.Context, newRequest) s.Equal(sdk.CodeType(107), res.Code) } diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index e3b333b1..31feb924 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -448,7 +448,7 @@ func (s *KeeperSuite) TestQueryGetRequest() { s.Equal(sdk.CodeType(601), err.Code()) // Set Request - err = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + err = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0, types.Local, nil) s.SDKNil(err) // Use querier handler to get request diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go index ffbbf9db..79ce9e82 100644 --- a/golang/x/relay/keeper/request_test.go +++ b/golang/x/relay/keeper/request_test.go @@ -9,7 +9,7 @@ import ( ) func (s *KeeperSuite) TestEmitProofRequest() { - s.Keeper.emitProofRequest(s.Context, []byte{0}, []byte{0}, 0, types.RequestID{}) + s.Keeper.emitProofRequest(s.Context, []byte{0}, []byte{0}, 0, types.RequestID{}, types.Local) events := s.Context.EventManager().Events() e := events[0] @@ -41,7 +41,7 @@ func (s *KeeperSuite) TestIncrementID() { func (s *KeeperSuite) TestHasRequest() { hasRequest := s.Keeper.hasRequest(s.Context, types.RequestID{}) s.Equal(false, hasRequest) - requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4) + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4, types.Local, nil) s.Nil(requestErr) hasRequest = s.Keeper.hasRequest(s.Context, types.RequestID{}) s.Equal(true, hasRequest) @@ -52,7 +52,7 @@ func (s *KeeperSuite) TestSetRequest() { idTag := []byte(types.RequestIDTag) store.Set(idTag, bytes.Repeat([]byte{9}, 9)) - err := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + err := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0, types.Local, nil) s.Equal(sdk.CodeType(107), err.Code()) } @@ -62,7 +62,7 @@ func (s *KeeperSuite) TestSetRequestState() { s.Equal(sdk.CodeType(601), activeErr.Code()) // set request - requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0) + requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0, types.Local, nil) s.Nil(requestErr) // change active state to false activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, false) @@ -79,7 +79,7 @@ func (s *KeeperSuite) TestGetRequest() { s.Equal(sdk.CodeType(601), err.Code()) s.Equal(types.ProofRequest{}, request) - requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0) + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0, types.Local, nil) s.Nil(requestErr) request, err = s.Keeper.getRequest(s.Context, types.RequestID{}) @@ -102,7 +102,7 @@ func (s *KeeperSuite) TestCheckRequests() { s.Equal(sdk.CodeType(601), err.Code()) // set request - requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0) + requestErr := s.Keeper.setRequest(s.Context, []byte{1}, []byte{1}, 0, 0, types.Local, nil) s.Nil(requestErr) // change active state to false activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) @@ -134,7 +134,7 @@ func (s *KeeperSuite) TestCheckRequests() { out, outErr := btcspv.ExtractOutputAtIndex(v.Vout, uint(v.OutputIdx)) s.Nil(outErr) // out[8:] extracts the output script which we use to set the request - requestErr = s.Keeper.setRequest(s.Context, []byte{0}, out[8:], 1000, 0) + requestErr = s.Keeper.setRequest(s.Context, []byte{0}, out[8:], 1000, 0, types.Local, nil) s.SDKNil(requestErr) err = s.Keeper.checkRequests( s.Context, @@ -146,7 +146,7 @@ func (s *KeeperSuite) TestCheckRequests() { s.Equal(sdk.CodeType(608), err.Code()) // Errors if input value does not equal spends value - requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{0}, 0, 255) + requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{0}, 0, 255, types.Local, nil) s.SDKNil(requestErr) err = s.Keeper.checkRequests( s.Context, @@ -162,7 +162,7 @@ func (s *KeeperSuite) TestCheckRequests() { s.Nil(extractErr) outpoint := btcspv.ExtractOutpoint(in) // out[8:] extracts the output script which we use to set the request - requestErr = s.Keeper.setRequest(s.Context, outpoint, out[8:], 10, 255) + requestErr = s.Keeper.setRequest(s.Context, outpoint, out[8:], 10, 255, types.Local, nil) s.SDKNil(requestErr) err = s.Keeper.checkRequests( s.Context, diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 28d59364..7675f4ce 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -11,8 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func (k Keeper) emitProofRequest(ctx sdk.Context, pays, spends []byte, paysValue uint64, id types.RequestID) { - ctx.EventManager().EmitEvent(types.NewProofRequestEvent(pays, spends, paysValue, id)) +func (k Keeper) emitProofRequest(ctx sdk.Context, pays, spends []byte, paysValue uint64, id types.RequestID, origin types.Origin) { + ctx.EventManager().EmitEvent(types.NewProofRequestEvent(pays, spends, paysValue, id, origin)) } func (k Keeper) getRequestStore(ctx sdk.Context) sdk.KVStore { @@ -24,7 +24,7 @@ func (k Keeper) hasRequest(ctx sdk.Context, id types.RequestID) bool { return store.Has(id[:]) } -func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValue uint64, numConfs uint8) sdk.Error { +func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValue uint64, numConfs uint8, origin types.Origin, action types.HexBytes) sdk.Error { store := k.getRequestStore(ctx) spendsDigest := btcspv.Hash256(spends) @@ -36,6 +36,8 @@ func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValu PaysValue: paysValue, ActiveState: true, NumConfs: numConfs, + Origin: origin, + Action: action, } // When a new request comes in, get the id and use it to store request @@ -57,7 +59,7 @@ func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValu } // Emit Proof Request event - k.emitProofRequest(ctx, pays, spends, request.PaysValue, id) + k.emitProofRequest(ctx, pays, spends, request.PaysValue, id, origin) return nil } diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go index 3120d41c..241dada2 100644 --- a/golang/x/relay/keeper/validator_test.go +++ b/golang/x/relay/keeper/validator_test.go @@ -67,7 +67,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { s.Keeper.ingestHeader(s.Context, validProof.Proof.ConfirmingHeader) s.Keeper.setLink(s.Context, validProof.Proof.ConfirmingHeader) s.Keeper.ingestHeader(s.Context, validProof.BestKnown) - requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4) + requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4, types.Local, nil) s.Nil(requestErr) // errors if getConfs fails @@ -98,7 +98,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { } // errors if number of confirmations is less than the number of confirmations on the request - requestErr = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 5) + requestErr = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 5, types.Local, nil) s.Nil(requestErr) copiedRequest := tc[0].FilledRequests diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index 17c22e9e..c461564d 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -188,6 +188,11 @@ const ( // NotEnoughConfsMessage is the corresponding message NotEnoughConfsMessage = "Not enough confirmations" + // ActionLength means the pays value is greater than 50 bytes + ActionLength sdk.CodeType = 612 + // ActionLengthMessage is the corresponding message + ActionLengthMessage = "Action value is greater than 500 bytes" + // 700-block External // ExternalError is an error from a dependency @@ -319,6 +324,11 @@ func ErrPaysLength(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, PaysLength, PaysLengthMessage) } +// ErrActionLength throws an error +func ErrActionLength(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, ActionLength, ActionLengthMessage) +} + // ErrInvalidVin throws an error func ErrInvalidVin(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, InvalidVin, InvalidVinMessage) diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 0ac2ed6a..10f6be93 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -24,6 +24,7 @@ const ( AttributeKeyPays = "pays" AttributeKeySpends = "spends" AttributeKeyPaysValue = "value" + AttributeKeyOrigin = "origin" ) // NewReorgEvent instantiates a reorg event @@ -46,12 +47,13 @@ func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { } // NewProofRequestEvent instantiates a proof request event -func NewProofRequestEvent(pays, spends []byte, paysValue uint64, id RequestID) sdk.Event { +func NewProofRequestEvent(pays, spends []byte, paysValue uint64, id RequestID, origin Origin) sdk.Event { return sdk.NewEvent( EventTypeProofRequest, sdk.NewAttribute(AttributeKeyRequestID, fmt.Sprintf("%d", id)), sdk.NewAttribute(AttributeKeyPays, "0x"+hex.EncodeToString(pays[:])), sdk.NewAttribute(AttributeKeySpends, "0x"+hex.EncodeToString(spends[:])), sdk.NewAttribute(AttributeKeyPaysValue, fmt.Sprintf("%d", paysValue)), + sdk.NewAttribute(AttributeKeyOrigin, fmt.Sprintf("%d", origin)), ) } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index dd97437a..c841d4b8 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -151,16 +151,20 @@ type MsgNewRequest struct { Pays []byte `json:"pays"` PaysValue uint64 `json:"paysValue"` NumConfs uint8 `json:"numConfs"` + Origin Origin `json:"origin"` + Action HexBytes `json:"action"` } // NewMsgNewRequest instantiates a MsgNewRequest -func NewMsgNewRequest(address sdk.AccAddress, spends, pays []byte, paysValue uint64, numConfs uint8) MsgNewRequest { +func NewMsgNewRequest(address sdk.AccAddress, spends, pays []byte, paysValue uint64, numConfs uint8, origin Origin, action HexBytes) MsgNewRequest { return MsgNewRequest{ address, spends, pays, paysValue, numConfs, + origin, + action, } } @@ -181,6 +185,9 @@ func (msg MsgNewRequest) ValidateBasic() sdk.Error { if len(msg.Pays) > 50 { return ErrPaysLength(DefaultCodespace) } + if len(msg.Action) > 500 { + return ErrActionLength(DefaultCodespace) + } return nil } diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go index af99c68b..52756e53 100644 --- a/golang/x/relay/types/requests.go +++ b/golang/x/relay/types/requests.go @@ -19,6 +19,8 @@ type ProofRequest struct { PaysValue uint64 `json:"paysValue"` ActiveState bool `json:"activeState"` NumConfs uint8 `json:"numConfs"` + Origin Origin `json:"origin"` + Action HexBytes `json:"action"` } // NewRequestID instantiates a RequestID from a byte slice diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 878f576b..bfe8c45e 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -26,6 +26,16 @@ type BitcoinHeader = btcspv.BitcoinHeader // SPVProof is the base struct for an SPV proof type SPVProof = btcspv.SPVProof +// Origin an enum of types denoting requests either from the local chain +// or a remote chain +type Origin int + +// Origin possible types +const ( + Local Origin = 0 + Remote Origin = 1 +) + // Hash256DigestFromHex converts a hex into a Hash256Digest func Hash256DigestFromHex(hexStr string) (Hash256Digest, sdk.Error) { data := hexStr From e0c44d5c8d5f8c39ad49f6487706357e533acb31 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 24 Mar 2020 15:19:57 -0700 Subject: [PATCH 066/148] chore: comment and lint cleanup --- golang/x/relay/keeper/headers.go | 1 - golang/x/relay/keeper/keeper_test.go | 8 ++++---- golang/x/relay/keeper/querier_test.go | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index f317798c..e36e66c3 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -238,7 +238,6 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H // IngestHeaderChain ingests a chain of headers func (k Keeper) IngestHeaderChain(ctx sdk.Context, headers []types.BitcoinHeader) sdk.Error { - // Find the anchor in our store return k.ingestHeaders(ctx, headers, false) } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 3f74fa79..c88740f4 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -263,20 +263,20 @@ func TestKeeper(t *testing.T) { suite.Run(t, keeperSuite) } -func (suite *KeeperSuite) SDKNil(e sdk.Error) { +func (s *KeeperSuite) SDKNil(e sdk.Error) { var msg string if e != nil { msg = e.Error() } - suite.Nil(e, msg) + s.Nil(e, msg) } -func (suite *KeeperSuite) EqualError(e sdk.Error, code int) { +func (s *KeeperSuite) EqualError(e sdk.Error, code int) { var msg string if e.Code() != sdk.CodeType(code) { msg = fmt.Sprintf("%sExpected: %d\n", e.Error(), code) } - suite.Equal(sdk.CodeType(code), e.Code(), msg) + s.Equal(sdk.CodeType(code), e.Code(), msg) } func (s *KeeperSuite) TestGetPrefixStore() { diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index 31feb924..66e9e881 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -8,7 +8,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) -func (suite *KeeperSuite) TestDecodeUint32FromPath() { +func (s *KeeperSuite) TestDecodeUint32FromPath() { DecodeUintPass := []struct { Path []string Idx int @@ -47,15 +47,15 @@ func (suite *KeeperSuite) TestDecodeUint32FromPath() { index := DecodeUintPass[i].Idx limit := DecodeUintPass[i].DefaultLimit num, err := decodeUint32FromPath(path, index, limit) - suite.SDKNil(err) - suite.Equal(num, DecodeUintPass[i].Output) + s.SDKNil(err) + s.Equal(num, DecodeUintPass[i].Output) } for i := range DecodeUintFail { path := DecodeUintFail[i].Path index := DecodeUintFail[i].Idx limit := DecodeUintFail[i].DefaultLimit _, err := decodeUint32FromPath(path, index, limit) - suite.Equal(DecodeUintFail[i].Err, err.Code()) + s.Equal(DecodeUintFail[i].Err, err.Code()) } } From a6a627cbc013e27dd84247c890716a2b57df7ead Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Wed, 25 Mar 2020 14:32:17 -0400 Subject: [PATCH 067/148] Update golang README (#46) --- golang/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/golang/README.md b/golang/README.md index 188915aa..bb2c7f2e 100644 --- a/golang/README.md +++ b/golang/README.md @@ -8,6 +8,10 @@ - - [X] Validate SPV Proofs - - [X] `ProvideProof` message - [ ] Milestone 3 +- - [ ] Provide tooling for manual testing (scripts, docs, json test files) +- - [ ] Integration Tests +- - [ ] Document relay design & architecture +- - [ ] Document public interface - - [ ] Provide hooks to execute tasks + dispatch messages - - [ ] Add a basic web dashboard with Relay health @@ -32,8 +36,8 @@ | IsMostRecentCommonAncestor | Completed | Determine if a block is the LCA of two headers| | HeaviestFromAncestor | Completed | Check which of two descendents is heaviest from the LCA | | GetRequest | Completed | Get details of an SPV Proof Request| -| CheckProof | TODO | Check the syntactic validity of an SPV Proof | -| CheckRequests | TODO | Perform CheckProof and check the SPV Proof against a set of Requests | +| CheckProof | Completed | Check the syntactic validity of an SPV Proof | +| CheckRequests | Completed | Perform CheckProof and check the SPV Proof against a set of Requests | ## How to add a view function (queries) 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` From 8c4e4028ca1a52153f50771f5a2b45ce198f2902 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Fri, 27 Mar 2020 16:44:10 -0400 Subject: [PATCH 068/148] Prepare Manual Testing (#45) * Change genesis state * CLI accepts raw json or json files as input * Begin writing scripts for CLI manual testing * fix json files * Issue47: Accept empty pays and spends * checkproof CLI call functioning * Start NewRequest Id's at 1 instead of 0 * update json * Update scripts/README.md * Take out comments * Bytes and stuff * Add make install to script * redundant check * bug: no default limit in marknewheavies, cli query ismostrecentcommonancestor dropped one arg * scripts/init_chain.sh exits if error with make install" * Easily customize genesis data * Add marknewheaviest to README.md Co-authored-by: James Prestwich --- golang/scripts/README.md | 83 +++++++++++++ golang/scripts/init_chain.sh | 26 ++++ .../scripts/json_data/0_new_difficulty.json | 22 ++++ golang/scripts/json_data/1_check_proof.json | 20 ++++ .../scripts/json_data/2_ingest_headers.json | 112 ++++++++++++++++++ .../scripts/json_data/3_filled_requests.json | 7 ++ .../scripts/json_data/4_ingest_headers.json | 62 ++++++++++ golang/scripts/json_data/genesis.json | 21 ++++ golang/x/relay/client/cli/query.go | 74 +++++++++--- golang/x/relay/client/cli/tx.go | 95 +++++++++++---- golang/x/relay/client/cli/utils.go | 14 +++ golang/x/relay/gen_state.go | 10 +- golang/x/relay/genesis.go | 9 +- golang/x/relay/keeper/chain.go | 1 - golang/x/relay/keeper/requests.go | 19 ++- golang/x/relay/types/codec.go | 1 + golang/x/relay/types/msgs.go | 10 +- golang/x/relay/types/querier.go | 2 +- 18 files changed, 534 insertions(+), 54 deletions(-) create mode 100644 golang/scripts/README.md create mode 100755 golang/scripts/init_chain.sh create mode 100644 golang/scripts/json_data/0_new_difficulty.json create mode 100644 golang/scripts/json_data/1_check_proof.json create mode 100644 golang/scripts/json_data/2_ingest_headers.json create mode 100644 golang/scripts/json_data/3_filled_requests.json create mode 100644 golang/scripts/json_data/4_ingest_headers.json create mode 100644 golang/scripts/json_data/genesis.json create mode 100644 golang/x/relay/client/cli/utils.go diff --git a/golang/scripts/README.md b/golang/scripts/README.md new file mode 100644 index 00000000..75a77fa8 --- /dev/null +++ b/golang/scripts/README.md @@ -0,0 +1,83 @@ +# Build and Run App + +## Setup +If you have never used the `go mod` before, you must add some parameters to your environment. + +```bash +mkdir -p $HOME/go/bin +echo "export GOBIN=\$GOPATH/bin" >> ~/.bash_profile +echo "export PATH=\$PATH:\$GOBIN" >> ~/.bash_profile +source ~/.bash_profile +``` + +Now, you can install and run the application. + +```bash +# Clone repository +git clone https://github.com/summa-tx/relays.git +cd relays/golang + +# Install the app into your $GOBIN +make install + +# Now you should be able to run the following commands: +relayd help +relaycli help +``` +## Running the CLI +To run the CLI for manual testing, you can run `./scripts/init_chain.sh` to initialize a new chain.

+All chain related data lives in `scripts/json_data`. Edit `scripts/json_data/genesis.json` to generate a customized genesis state. This JSON must be a list of block headers pertaining to one epoch. The first header must be the first block of the epoch. The remaining headers must be ordered headers beginning at any height in the epoch. +```bash +# Set the executable rights if not done already +chmod +x scripts/init_chain.sh + +#run script +./scripts/init_chain.sh +``` +Open up a new terminal tab in the same directory to begin interacting with the chain. As per the setup script, you can now interact via username/password `me / 12345678` such that when submitting transactions using flag `--from me` when prompted for the password enter: `12345678` + +### Query CLI +Querying neither requires the `--from` flag nor a password. +```bash +# Retrieve the first digest of the relay +relaycli query relay getrelaygenesis + +# Retrieve the best known digest +relaycli query relay getlastreorglca + +# List other query options +relaycli query relay +``` + +### Transact with CLI +Transactions require the `--from` flag and password.

+JSON parameters can be accepted as either raw json or json files. including the `--inputfile` flag will interpret all json parameters as json files from directory `scripts/json_data`

+use the flag ` --broadcast-mode block` to get errors synchronously upon transactions. Otherwise errors could get swallowed resulting in false positive success

+Here are some transactions and queries you can run upon initializing the chain with the default genesis state: + +```bash +# Add the following bitcoin headers which also correspond with a difficulty change in the bitcoin change +relaycli tx relay ingestdiffchange ef8248820b277b542ac2a726ccd293e8f2a3ea24c1fe04000000000000000000 0_new_difficulty.json --inputfile --from me --broadcast-mode block + +# Submit Proof Request +relaycli tx relay newrequest 0x 0x17a91423737cd98bb6b2da5a11bcd82e5de36591d69f9f87 0 1 --broadcast-mode block --from me + +# Check whether given proof is valid: It will not because block with transaction has not been ingested yet +relaycli query relay checkproof 1_check_proof.json --inputfile + +# Ingest new headers to relay (without any change in difficulty) +relaycli tx relay ingestheaders 2_ingest_headers.json --from me --inputfile --broadcast-mode block + +# Check whether given proof is valid: It will will be valid with new headers from previous tx +relaycli query relay checkproof 1_check_proof.json --inputfile + +# Provide valid proof that fulfils a proof request +relaycli tx relay provideproof 1_check_proof.json 3_filled_requests.json --from me --inputfile --broadcast-mode block + +# Ingest remaining headers to relay (without any change in difficulty) +relaycli tx relay ingestheaders 4_ingest_headers.json --from me --inputfile --broadcast-mode block + +# Mark new heaviest to update the best known digest +relaycli tx relay marknewheaviest 0x4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000 0x0000c020954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618dd150425eff3212173f0c982d 0x0000c020bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d1090000000000000000008a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3b971425ed41a12174334a315 0 --broadcast-mode block --from me + +``` diff --git a/golang/scripts/init_chain.sh b/golang/scripts/init_chain.sh new file mode 100755 index 00000000..0a43b35f --- /dev/null +++ b/golang/scripts/init_chain.sh @@ -0,0 +1,26 @@ +#!/bin/bash +rm -rf ~/.summa/cosmosrelay + +if make install +then + echo SUCCESSFULLY BUILT +else + echo ERROR: CANNOT BUILD + exit 1 +fi + +relayd init mynode --chain-id relay + +echo "12345678" | relaycli keys add me + +relayd add-genesis-account $(relaycli keys show me -a) 1000cbtc,100000000stake + +relaycli config chain-id relay +relaycli config output json +relaycli config indent true +relaycli config trust-node true + +echo "12345678" | relayd gentx --name me +relayd collect-gentxs + +relayd start diff --git a/golang/scripts/json_data/0_new_difficulty.json b/golang/scripts/json_data/0_new_difficulty.json new file mode 100644 index 00000000..6f73a148 --- /dev/null +++ b/golang/scripts/json_data/0_new_difficulty.json @@ -0,0 +1,22 @@ +[ + { + "raw": "0000ff3f4c2078d0388e3844fe6241723e9543074bd3a974c166110000000000000000000b43965e06a087b47726f35aa886df5630057215bb46a58216243e236565ac296053425ed41a121781b98236", + "hash": "00000000000000000004f0d5a56473157602071a4bb112623b1211536d40e69b", + "hash_le": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", + "height": 616896, + "prevhash": "0000000000000000001166c174a9d34b0743953e724162fe44388e38d078204c", + "prevhash_le": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", + "merkle_root": "29ac6565233e241682a546bb1572053056df86a85af32677b487a0065e96430b", + "merkle_root_le": "0b43965e06a087b47726f35aa886df5630057215bb46a58216243e236565ac29" + }, + { + "raw": "00c0ff3f9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000f19f16556e7952046ea684eb6e8aeebe5fc1bb7fca09a0633fd3da5ee9e2dbb80c56425ed41a12172fdc7d2c", + "hash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", + "hash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "height": 616897, + "prevhash": "00000000000000000004f0d5a56473157602071a4bb112623b1211536d40e69b", + "prevhash_le": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", + "merkle_root": "b8dbe2e95edad33f63a009ca7fbbc15fbeee8a6eeb84a66e0452796e55169ff1", + "merkle_root_le": "f19f16556e7952046ea684eb6e8aeebe5fc1bb7fca09a0633fd3da5ee9e2dbb8" + } + ] diff --git a/golang/scripts/json_data/1_check_proof.json b/golang/scripts/json_data/1_check_proof.json new file mode 100644 index 00000000..b984ed5a --- /dev/null +++ b/golang/scripts/json_data/1_check_proof.json @@ -0,0 +1,20 @@ +{ + "version": "02000000", + "vin": "01bbfe169156ef6922af60eced842fd588db83b1803d75f94b3054ab20c36729c20000000017160014761503a7b541c34ab017fe95d4e56b872af45319feffffff", + "vout": "023a44090a0000000017a91423737cd98bb6b2da5a11bcd82e5de36591d69f9f87b0c412000000000017a9143b77f8e86b4b65b4173a6a489b067dc2d14e49ce87", + "locktime": "c1690900", + "tx_id": "7dfc00bb2873727ee2bcb9ae448e90561c3476afeb04fabfeb48b0f9837d14f2", + "tx_id_le": "f2147d83f9b048ebbffa04ebaf76341c56908e44aeb9bce27e727328bb00fc7d", + "index": 13, + "confirming_header": { + "raw": "00e000200641238051855d1759da9b6603b156684a68a146d36a09000000000000000000f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4be58425ed41a1217d632cdcc", + "hash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", + "hash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "height": 616898, + "prevhash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", + "prevhash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "merkle_root": "a4ab3f68a57d5d743b5a02bfb551baed59a9fc17281605b886a2ae47f3d7f5f1", + "merkle_root_le": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" + }, + "intermediate_nodes": "58088fd963c83b6f8605a2e36230039f2f0327838984a439fbd02a747068f1394e0592da283da5fec881377e14a6a4952b8aafe69eef0f79249228b8d3774b21047d13d55d8565a84c7ec5bb66e677a322a197bd582eb8033163560a63423e919ecfe60a2c3d5a16e82faf72b1c5318a1b4c9fbc33945980b45b0b703d8c3cf4892ee4da7182112658c372a27adafae0588bf12242c7ee10eac9e2ca406b92cfd23133c8b957b6f52ff2c25b836b34c3587ea0c756cb994072b44199a718d913ab51c5c74aaab591cc846370692b17a7378c8aee6e1d46020d04b00208ab9fe0a545ef7072d6304c15005a2c3d4d77c94f95d2c2fa8bc00894d0eea0b7dda08333e53e219616c479448c43bf7df9463f600eac9380d257fdb85b64d3a627f335a535867e0f5d20a39a9ae95c55ab6ffddecc94530a95bd877b6583a37d635f2f8ff007761bc8623ce3a1bb93cbd1f65acaadbf419a56abd5ce00a94305ea8b5fcfa5f5e0bb8ae134a445e4ff3c650be2bdc2d07f8ad65670ccf6b0077e90f5b7" +} diff --git a/golang/scripts/json_data/2_ingest_headers.json b/golang/scripts/json_data/2_ingest_headers.json new file mode 100644 index 00000000..1ae3d72f --- /dev/null +++ b/golang/scripts/json_data/2_ingest_headers.json @@ -0,0 +1,112 @@ +[ + { + "raw": "00e000200641238051855d1759da9b6603b156684a68a146d36a09000000000000000000f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4be58425ed41a1217d632cdcc", + "hash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", + "hash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "height": 616898, + "prevhash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", + "prevhash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "merkle_root": "a4ab3f68a57d5d743b5a02bfb551baed59a9fc17281605b886a2ae47f3d7f5f1", + "merkle_root_le": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" + }, + { + "raw": "0000ff3f7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf090000000000000000000c2aeb41cdea44a03b437fcd294805092c294d613f7f7192e6ffc9c6ec9130cddf58425ed41a121783dd5b80", + "hash": "00000000000000000002c7b080fec48d40ce8d686e2cf0d011ddb8c648326b39", + "hash_le": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", + "height": 616899, + "prevhash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", + "prevhash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "merkle_root": "cd3091ecc6c9ffe692717f3f614d292c09054829cd7f433ba044eacd41eb2a0c", + "merkle_root_le": "0c2aeb41cdea44a03b437fcd294805092c294d613f7f7192e6ffc9c6ec9130cd" + }, + { + "raw": "00e0ff3f396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c70200000000000000000033b53cacf908eb0763918b833b80936d88c3bfa6999c280450d03855208791691f5a425ed41a1217940afef1", + "hash": "0000000000000000000cb8dcb46277f11dc04f0f34c18e8e2ee4bf8f1f9a2413", + "hash_le": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", + "height": 616900, + "prevhash": "00000000000000000002c7b080fec48d40ce8d686e2cf0d011ddb8c648326b39", + "prevhash_le": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", + "merkle_root": "699187205538d05004289c99a6bfc3886d93803b838b916307eb08f9ac3cb533", + "merkle_root_le": "33b53cacf908eb0763918b833b80936d88c3bfa6999c280450d0385520879169" + }, + { + "raw": "00e0002013249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000a9a61a4be4289169e82e9ba5ac35b9680cb6f9cb4bea0ae5b1b2ab6cda7eaac6745b425ed41a1217ddcacc90", + "hash": "000000000000000000025ad6dccc7192e070961b1dc011edb653f43765ba8888", + "hash_le": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", + "height": 616901, + "prevhash": "0000000000000000000cb8dcb46277f11dc04f0f34c18e8e2ee4bf8f1f9a2413", + "prevhash_le": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", + "merkle_root": "c6aa7eda6cabb2b1e50aea4bcbf9b60c68b935aca59b2ee8699128e44b1aa6a9", + "merkle_root_le": "a9a61a4be4289169e82e9ba5ac35b9680cb6f9cb4bea0ae5b1b2ab6cda7eaac6" + }, + { + "raw": "00e0ff3f8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000b2988e5fd8ef3af6aa08e1724147bd371c572bf80d86ad78a2bb3350f675c3a27d62425ed41a1217d68e19fd", + "hash": "0000000000000000000cdb8e560dfaa995928e2621fdb85e017996093baa87ea", + "hash_le": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", + "height": 616902, + "prevhash": "000000000000000000025ad6dccc7192e070961b1dc011edb653f43765ba8888", + "prevhash_le": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", + "merkle_root": "a2c375f65033bba278ad860df82b571c37bd474172e108aaf63aefd85f8e98b2", + "merkle_root_le": "b2988e5fd8ef3af6aa08e1724147bd371c572bf80d86ad78a2bb3350f675c3a2" + }, + { + "raw": "00008020ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c0000000000000000008ebb3bf274b7ab7f299279f3086dd59f5d45e4dc116db4d470c38dd2ff2b2c8f5b65425ed41a12174bd195f8", + "hash": "0000000000000000000b8369116e8168ecd130c9dcc44e6d10c5dcb92cb45547", + "hash_le": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", + "height": 616903, + "prevhash": "0000000000000000000cdb8e560dfaa995928e2621fdb85e017996093baa87ea", + "prevhash_le": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", + "merkle_root": "8f2c2bffd28dc370d4b46d11dce4455d9fd56d08f37992297fabb774f23bbb8e", + "merkle_root_le": "8ebb3bf274b7ab7f299279f3086dd59f5d45e4dc116db4d470c38dd2ff2b2c8f" + }, + { + "raw": "0000c0204755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b00000000000000000040fd126a7fac3d39cd65b7aa87df4785fe3e585eac589a56578cb4e6a42824bdae68425ed41a1217155cc584", + "hash": "0000000000000000000d8fc9184374dfdc808fc5a7e3409d9e42ed06a5a002ab", + "hash_le": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", + "height": 616904, + "prevhash": "0000000000000000000b8369116e8168ecd130c9dcc44e6d10c5dcb92cb45547", + "prevhash_le": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", + "merkle_root": "bd2428a4e6b48c57569a58ac5e583efe8547df87aab765cd393dac7f6a12fd40", + "merkle_root_le": "40fd126a7fac3d39cd65b7aa87df4785fe3e585eac589a56578cb4e6a42824bd" + }, + { + "raw": "00e0ff7fab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000d338bb7242ca738d8cb0ef0f05c4d888c52eb5a075041ffafbabc3268f041544166b425ed41a12178cd64a96", + "hash": "0000000000000000000962d551d6269edda97e5ff94586c84514f8cc7de1b50e", + "hash_le": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", + "height": 616905, + "prevhash": "0000000000000000000d8fc9184374dfdc808fc5a7e3409d9e42ed06a5a002ab", + "prevhash_le": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", + "merkle_root": "4415048f26c3abfbfa1f0475a0b52ec588d8c4050fefb08c8d73ca4272bb38d3", + "merkle_root_le": "d338bb7242ca738d8cb0ef0f05c4d888c52eb5a075041ffafbabc3268f041544" + }, + { + "raw": "00c0ff3f0eb5e17dccf81445c88645f95f7ea9dd9e26d651d5620900000000000000000064b53b8a3e102a1b7e7cbb245dd1339e87d34abcea730009bd2a2dca93683a71426f425ed41a12177b11d4dd", + "hash": "00000000000000000011de2a467a8d8dc58af1a6ba4e0b527edef805f83cfadd", + "hash_le": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", + "height": 616906, + "prevhash": "0000000000000000000962d551d6269edda97e5ff94586c84514f8cc7de1b50e", + "prevhash_le": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", + "merkle_root": "713a6893ca2d2abd090073eabc4ad3879e33d15d24bb7c7e1b2a103e8a3bb564", + "merkle_root_le": "64b53b8a3e102a1b7e7cbb245dd1339e87d34abcea730009bd2a2dca93683a71" + }, + { + "raw": "00e00020ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade110000000000000000000d3e06e5e25b12623a9d6cd9f2c0b6df31315b64934a1e9b61edcc90e9f4ae275870425ed41a1217a1371395", + "hash": "00000000000000000009d103820ebb8000991df70f5b4750880e1bfb0fd400bc", + "hash_le": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", + "height": 616907, + "prevhash": "00000000000000000011de2a467a8d8dc58af1a6ba4e0b527edef805f83cfadd", + "prevhash_le": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", + "merkle_root": "27aef4e990cced619b1e4a93645b3131dfb6c0f2d96c9d3a62125be2e5063e0d", + "merkle_root_le": "0d3e06e5e25b12623a9d6cd9f2c0b6df31315b64934a1e9b61edcc90e9f4ae27" + }, + { + "raw": "0000c020bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d1090000000000000000008a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3b971425ed41a12174334a315", + "hash": "00000000000000000003756d91d26f6322212607bfb6e35d7e02e4bf38a0d0f8", + "hash_le": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", + "height": 616908, + "prevhash": "00000000000000000009d103820ebb8000991df70f5b4750880e1bfb0fd400bc", + "prevhash_le": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", + "merkle_root": "e3c7facde577ff104537a9a6658674e65feabcd61b744ced1030c57c377b318a", + "merkle_root_le": "8a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3" + } +] diff --git a/golang/scripts/json_data/3_filled_requests.json b/golang/scripts/json_data/3_filled_requests.json new file mode 100644 index 00000000..79aaaaa5 --- /dev/null +++ b/golang/scripts/json_data/3_filled_requests.json @@ -0,0 +1,7 @@ +[ + { + "inputIndex": 0, + "outputIndex": 0, + "id": "0x0000000000000000" + } +] diff --git a/golang/scripts/json_data/4_ingest_headers.json b/golang/scripts/json_data/4_ingest_headers.json new file mode 100644 index 00000000..a40da1f5 --- /dev/null +++ b/golang/scripts/json_data/4_ingest_headers.json @@ -0,0 +1,62 @@ +[ + { + "raw": "00e0ff2ff8d0a038bfe4027e5de3b6bf07262122636fd2916d75030000000000000000009c66fd29c230fbc348ba962f1c2ca8c6c9bca2cd01fd55006f31d65d0ed139016572425ed41a1217c6c10ed1", + "hash": "00000000000000000002405dc45827d64aabf25ac548984f4f7f94a89c4cda5e", + "hash_le": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", + "height": 616909, + "prevhash": "00000000000000000003756d91d26f6322212607bfb6e35d7e02e4bf38a0d0f8", + "prevhash_le": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", + "merkle_root": "0139d10e5dd6316f0055fd01cda2bcc9c6a82c1c2f96ba48c3fb30c229fd669c", + "merkle_root_le": "9c66fd29c230fbc348ba962f1c2ca8c6c9bca2cd01fd55006f31d65d0ed13901" + }, + { + "raw": "000000205eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d40020000000000000000002af75f1c0581dfaf0aa7007deec24ef232fd280d60a16a2ab26ee8dd509c746bcd74425ed41a1217f47350e8", + "hash": "00000000000000000007296d7e65357a8157c66d647703338f96305d0e0344f4", + "hash_le": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", + "height": 616910, + "prevhash": "00000000000000000002405dc45827d64aabf25ac548984f4f7f94a89c4cda5e", + "prevhash_le": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", + "merkle_root": "6b749c50dde86eb22a6aa1600d28fd32f24ec2ee7d00a70aafdf81051c5ff72a", + "merkle_root_le": "2af75f1c0581dfaf0aa7007deec24ef232fd280d60a16a2ab26ee8dd509c746b" + }, + { + "raw": "00e00020f444030e5d30968f330377646dc657817a35657e6d29070000000000000000007ec6b452207ab96612f2b3231325877fa83b814a03e2069bac136f2f0ac42435e176425ed41a12173c2750a5", + "hash": "00000000000000000007ff64bae73f65f9b5b932b1e592eea27fc7b6bfcefe0e", + "hash_le": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", + "height": 616911, + "prevhash": "00000000000000000007296d7e65357a8157c66d647703338f96305d0e0344f4", + "prevhash_le": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", + "merkle_root": "3524c40a2f6f13ac9b06e2034a813ba87f87251323b3f21266b97a2052b4c67e", + "merkle_root_le": "7ec6b452207ab96612f2b3231325877fa83b814a03e2069bac136f2f0ac42435" + }, + { + "raw": "00e000200efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff070000000000000000007d1740590d7b907cec752812c3202fed4f1c39ab565d27867981bc6b4d4a20b28578425ed41a12170984291f", + "hash": "00000000000000000010816dffafe360e8ad742120a77b9c477685e9587ab6f1", + "hash_le": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", + "height": 616912, + "prevhash": "00000000000000000007ff64bae73f65f9b5b932b1e592eea27fc7b6bfcefe0e", + "prevhash_le": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", + "merkle_root": "b2204a4d6bbc817986275d56ab391c4fed2f20c3122875ec7c907b0d5940177d", + "merkle_root_le": "7d1740590d7b907cec752812c3202fed4f1c39ab565d27867981bc6b4d4a20b2" + }, + { + "raw": "00000020f1b67a58e98576479c7ba7202174ade860e3afff6d81100000000000000000009293b2d0c8f430a39cc8346c5478df790027176b5d26b0893a088ff6179b63108a7a425ed41a12173041448e", + "hash": "0000000000000000000bbead2875713f0136e921d879bc958f31d39df37b6751", + "hash_le": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", + "height": 616913, + "prevhash": "00000000000000000010816dffafe360e8ad742120a77b9c477685e9587ab6f1", + "prevhash_le": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", + "merkle_root": "10639b17f68f083a89b0265d6b17270079df78546c34c89ca330f4c8d0b29392", + "merkle_root_le": "9293b2d0c8f430a39cc8346c5478df790027176b5d26b0893a088ff6179b6310" + }, + { + "raw": "0000002051677bf39dd3318f95bc79d821e936013f717528adbe0b0000000000000000008bdd1ff50a88a852d497061c51542db436818bd3b6b2455c0f6333d7533cffd1387c425ed41a12175f902567", + "hash": "000000000000000000104f6120e4f469427f347555b1f37251fe90f94b33f0e6", + "hash_le": "e6f0334bf990fe5172f3b15575347f4269f4e420614f10000000000000000000", + "height": 616914, + "prevhash": "0000000000000000000bbead2875713f0136e921d879bc958f31d39df37b6751", + "prevhash_le": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", + "merkle_root": "d1ff3c53d733630f5c45b2b6d38b8136b42d54511c0697d452a8880af51fdd8b", + "merkle_root_le": "8bdd1ff50a88a852d497061c51542db436818bd3b6b2455c0f6333d7533cffd1" + } +] diff --git a/golang/scripts/json_data/genesis.json b/golang/scripts/json_data/genesis.json new file mode 100644 index 00000000..ea2efdfa --- /dev/null +++ b/golang/scripts/json_data/genesis.json @@ -0,0 +1,21 @@ +[ + { + "raw": "00c0ff3fcc235a9993cbdfe938b54b9850485c46b6a408dd23fe100000000000000000006d79472dd0cf809ff632565b6de2fb051f5f87a06a5daf79bb95412f357f434353f42f5eff32121761139949", + "hash": "00000000000000000004fec124eaa3f2e893d2cc26a7c22a547b270b824882ef", + "hash_le": "ef8248820b277b542ac2a726ccd293e8f2a3ea24c1fe04000000000000000000", + "height": 614880, + "prevhash": "00000000000000000010fe23dd08a4b6465c4850984bb538e9dfcb93995a23cc", + "prevhash_le": "cc235a9993cbdfe938b54b9850485c46b6a408dd23fe10000000000000000000", + "merkle_root": "43437f352f4195bb79af5d6aa0875f1f05fbe26d5b5632f69f80cfd02d47796d", + "merkle_root_le": "6d79472dd0cf809ff632565b6de2fb051f5f87a06a5daf79bb95412f357f4343"}, + { + "raw": "0000c020954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618dd150425eff3212173f0c982d", + "hash": "0000000000000000001166c174a9d34b0743953e724162fe44388e38d078204c", + "hash_le": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", + "height": 616895, + "prevhash": "0000000000000000000c9610f5cd595f405a025e2060c2d54fc3ab80d9a14e95", + "prevhash_le": "954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000", + "merkle_root": "8d61daf6f04474eda372ab2ad7e74679a0d36d93c445da97e514caa6044d86ad", + "merkle_root_le": "ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618d" + } +] diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index 1b78a407..e911426d 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/summa-tx/relays/golang/x/relay/types" ) @@ -28,6 +29,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { GetCmdFindAncestor(queryRoute, cdc), GetCmdIsMostRecentCommonAncestor(queryRoute, cdc), GetCmdHeaviestFromAncestor(queryRoute, cdc), + GetCmdCheckProof(queryRoute, cdc), )...) return relayQueryCommand } @@ -119,7 +121,7 @@ func GetCmdGetRelayGenesis(queryRoute string, cdc *codec.Codec) *cobra.Command { } } -// GetCmdIsAncestor returns the CLI command struct for IsAncestor +// GetCmdGetLastReorgLCA returns the CLI command struct for GetLastReorgLCA func GetCmdGetLastReorgLCA(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "getlastreorglca", @@ -142,6 +144,7 @@ func GetCmdGetLastReorgLCA(queryRoute string, cdc *codec.Codec) *cobra.Command { } } +// GetCmdFindAncestor returns the CLI command struct for FindAncestor func GetCmdFindAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "findancestor ", @@ -190,7 +193,7 @@ func GetCmdFindAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { } } -// GetCmdIsAncestor returns the CLI command struct for IsAncestor +// GetCmdIsMostRecentCommonAncestor returns the CLI command struct for IsMostRecentCommonAncestor func GetCmdIsMostRecentCommonAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "ismostrecentcommonancestor [limit]", @@ -212,7 +215,7 @@ func GetCmdIsMostRecentCommonAncestor(queryRoute string, cdc *codec.Codec) *cobr return nil } - right, sdkErr := types.Hash256DigestFromHex(args[1]) + right, sdkErr := types.Hash256DigestFromHex(args[2]) if sdkErr != nil { fmt.Print(sdkErr.Error()) return nil @@ -359,9 +362,9 @@ func GetCmdGetRequest(queryRoute string, cdc *codec.Codec) *cobra.Command { } } -// GetCmdCheckRequest returns the CLI command struct for checkRequests +// GetCmdCheckRequests returns the CLI command struct for checkRequests func GetCmdCheckRequests(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "checkrequests ", Long: "check whether proof successfully validates a set of requests", Args: cobra.ExactArgs(2), @@ -369,15 +372,33 @@ func GetCmdCheckRequests(queryRoute string, cdc *codec.Codec) *cobra.Command { cliCtx := context.NewCLIContext().WithCodec(cdc) var proof types.SPVProof - jsonErr := json.Unmarshal([]byte(args[0]), &proof) - if jsonErr != nil { - return jsonErr - } - var requests []types.FilledRequestInfo - jsonErr = json.Unmarshal([]byte(args[1]), &requests) - if jsonErr != nil { - return jsonErr + if viper.GetBool("inputfile") { + jsonFileProof, err := readJSONFromFile(args[0]) + if err != nil { + return err + } + jsonFileReq, err := readJSONFromFile(args[1]) + if err != nil { + return err + } + jsonErr := json.Unmarshal([]byte(jsonFileProof), &proof) + if jsonErr != nil { + return jsonErr + } + jsonErr = json.Unmarshal([]byte(jsonFileReq), &requests) + if jsonErr != nil { + return jsonErr + } + } else { + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } + jsonErr = json.Unmarshal([]byte(args[1]), &requests) + if jsonErr != nil { + return jsonErr + } } filledRequests := types.NewFilledRequests( @@ -407,21 +428,35 @@ func GetCmdCheckRequests(queryRoute string, cdc *codec.Codec) *cobra.Command { return cliCtx.PrintOutput(&out) }, } + + attachFlagFileinput(cmd) + return cmd } // GetCmdCheckProof returns the CLI command struct for checkProof func GetCmdCheckProof(queryRoute string, cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "checkproof ", Long: "check proof has valid parameters", - Args: cobra.ExactArgs(2), + Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) var proof types.SPVProof - jsonErr := json.Unmarshal([]byte(args[0]), &proof) - if jsonErr != nil { - return jsonErr + if viper.GetBool("inputfile") { + jsonFile, err := readJSONFromFile(args[0]) + if err != nil { + return err + } + jsonErr := json.Unmarshal([]byte(jsonFile), &proof) + if jsonErr != nil { + return jsonErr + } + } else { + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } } params := types.QueryParamsCheckProof{ @@ -446,4 +481,7 @@ func GetCmdCheckProof(queryRoute string, cdc *codec.Codec) *cobra.Command { return cliCtx.PrintOutput(&out) }, } + + attachFlagFileinput(cmd) + return cmd } diff --git a/golang/x/relay/client/cli/tx.go b/golang/x/relay/client/cli/tx.go index 32a0abe3..c89efafa 100644 --- a/golang/x/relay/client/cli/tx.go +++ b/golang/x/relay/client/cli/tx.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -30,6 +31,10 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { relayTxCmd.AddCommand(client.PostCommands( GetCmdIngestHeaderChain(cdc), + GetCmdIngestDifficultyChange(cdc), + GetCmdNewRequest(cdc), + GetCmdProvideProof(cdc), + GetCmdMarkNewHeaviest(cdc), )...) return relayTxCmd @@ -37,21 +42,31 @@ func GetTxCmd(storeKey string, cdc *codec.Codec) *cobra.Command { // GetCmdIngestHeaderChain creates a CLI command to ingest a header chain func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "ingestheaders ", Short: "Ingest a set of headers", - Long: "Ingest a set of headers. The headers must be in order, and the header immediately before the first must already be known to the relay", + Long: "Ingest a set of headers. The headers must be in order, and the header immediately before the first must already be known to the relay.\nUse flag --inputfile to submit a json filename as input from scripts/seed_data directory", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) - // note: unmarshalling json here is undesirable, but necessary - var headers []types.BitcoinHeader - jsonErr := json.Unmarshal([]byte(args[0]), &headers) - if jsonErr != nil { - return jsonErr + var headers = make([]types.BitcoinHeader, 0) + + if viper.GetBool("inputfile") { + jsonFile, err := readJSONFromFile(args[0]) + if err != nil { + return err + } + jsonErr := json.Unmarshal([]byte(jsonFile), &headers) + if jsonErr != nil { + return jsonErr + } + } else { + jsonErr := json.Unmarshal([]byte(args[0]), &headers) + if jsonErr != nil { + return jsonErr + } } msg := types.NewMsgIngestHeaderChain( @@ -67,11 +82,14 @@ func GetCmdIngestHeaderChain(cdc *codec.Codec) *cobra.Command { }, } + + attachFlagFileinput(cmd) + return cmd } // GetCmdIngestDifficultyChange creates a CLI command to ingest a difficulty change func GetCmdIngestDifficultyChange(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "ingestdiffchange ", Short: "Ingest a difficulty change.", Long: "Ingest a difficulty change. Prev Epoch Start is a hex digest.", @@ -88,9 +106,20 @@ func GetCmdIngestDifficultyChange(cdc *codec.Codec) *cobra.Command { } var headers []types.BitcoinHeader - jsonErr := json.Unmarshal([]byte(args[1]), &headers) - if jsonErr != nil { - return jsonErr + if viper.GetBool("inputfile") { + jsonFile, err := readJSONFromFile(args[1]) + if err != nil { + return err + } + jsonErr := json.Unmarshal([]byte(jsonFile), &headers) + if jsonErr != nil { + return jsonErr + } + } else { + jsonErr := json.Unmarshal([]byte(args[1]), &headers) + if jsonErr != nil { + return jsonErr + } } msg := types.NewMsgIngestDifficultyChange( @@ -107,6 +136,9 @@ func GetCmdIngestDifficultyChange(cdc *codec.Codec) *cobra.Command { }, } + + attachFlagFileinput(cmd) + return cmd } // GetCmdNewRequest stores a new proof request @@ -154,7 +186,7 @@ func GetCmdNewRequest(cdc *codec.Codec) *cobra.Command { // GetCmdProvideProof stores a new proof request func GetCmdProvideProof(cdc *codec.Codec) *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "provideproof ", Short: "validates proof of given requests", Long: "validates proof of given requests", @@ -164,15 +196,33 @@ func GetCmdProvideProof(cdc *codec.Codec) *cobra.Command { txBldr := auth.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) var proof types.SPVProof - jsonErr := json.Unmarshal([]byte(args[0]), &proof) - if jsonErr != nil { - return jsonErr - } - var requests []types.FilledRequestInfo - jsonErr = json.Unmarshal([]byte(args[1]), &requests) - if jsonErr != nil { - return jsonErr + if viper.GetBool("inputfile") { + jsonFileProof, err := readJSONFromFile(args[0]) + if err != nil { + return err + } + jsonFileReq, err := readJSONFromFile(args[1]) + if err != nil { + return err + } + jsonErr := json.Unmarshal([]byte(jsonFileProof), &proof) + if jsonErr != nil { + return jsonErr + } + jsonErr = json.Unmarshal([]byte(jsonFileReq), &requests) + if jsonErr != nil { + return jsonErr + } + } else { + jsonErr := json.Unmarshal([]byte(args[0]), &proof) + if jsonErr != nil { + return jsonErr + } + jsonErr = json.Unmarshal([]byte(args[1]), &requests) + if jsonErr != nil { + return jsonErr + } } filledRequests := types.NewFilledRequests( @@ -194,6 +244,9 @@ func GetCmdProvideProof(cdc *codec.Codec) *cobra.Command { }, } + + attachFlagFileinput(cmd) + return cmd } // GetCmdMarkNewHeaviest creates a CLI command to update best known digest and LCA diff --git a/golang/x/relay/client/cli/utils.go b/golang/x/relay/client/cli/utils.go new file mode 100644 index 00000000..357e4d3a --- /dev/null +++ b/golang/x/relay/client/cli/utils.go @@ -0,0 +1,14 @@ +package cli + +import ( + "github.com/spf13/cobra" + "io/ioutil" +) + +func readJSONFromFile(filename string) ([]byte, error) { + return ioutil.ReadFile("scripts/json_data/" + filename) +} + +func attachFlagFileinput(cmd *cobra.Command) { + cmd.Flags().Bool("inputfile", false, "Accepts a file as input for each json parameter") +} diff --git a/golang/x/relay/gen_state.go b/golang/x/relay/gen_state.go index 25876732..60076f06 100644 --- a/golang/x/relay/gen_state.go +++ b/golang/x/relay/gen_state.go @@ -2,15 +2,19 @@ package relay import ( "encoding/json" + "io/ioutil" ) -const headerJSON string = "[{\"raw\":\"000000208e244d2c55bc403caa5d6eaf0f922170e413eb1e02fb02000000000000000000e03b4d9df72d8db232a20bb2ff35c433a99f1467f391f75b5f62180d96f06d6aa4c4d65d3eb215179ef91633\",\"hash\":\"00000000000000000012145f8ffa7218d2d04ca66b61835a2a5eaec33dffc098\",\"hash_le\":\"98c0ff3dc3ae5e2a5a83616ba64cd0d21872fa8f5f1412000000000000000000\",\"height\":604800,\"prevhash\":\"00000000000000000002fb021eeb13e47021920faf6e5daa3c40bc552c4d248e\",\"prevhash_le\":\"8e244d2c55bc403caa5d6eaf0f922170e413eb1e02fb02000000000000000000\",\"merkle_root\":\"6a6df0960d18625f5bf791f367149fa933c435ffb20ba232b28d2df79d4d3be0\",\"merkle_root_le\":\"e03b4d9df72d8db232a20bb2ff35c433a99f1467f391f75b5f62180d96f06d6a\"},{\"raw\":\"00e0ff3f4170b40ea2cecf83619c940a31ad58cdcbac7cff509a0f0000000000000000000c241812286f0501002b30a10a554ef292922834e2a7d2bcaec3a50bc6a132d54121e45d3eb215175b183342\",\"hash\":\"00000000000000000004669f8082b56686b42523e58b1a8ce3f14830f38f5148\",\"hash_le\":\"48518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000\",\"height\":606210,\"prevhash\":\"0000000000000000000f9a50ff7caccbcd58ad310a949c6183cfcea20eb47041\",\"prevhash_le\":\"4170b40ea2cecf83619c940a31ad58cdcbac7cff509a0f000000000000000000\",\"merkle_root\":\"d532a1c60ba5c3aebcd2a7e234289292f24e550aa1302b0001056f281218240c\",\"merkle_root_le\":\"0c241812286f0501002b30a10a554ef292922834e2a7d2bcaec3a50bc6a132d5\"},{\"raw\":\"0000002048518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000b6cb41ffd9262fb97f8d0d95bfcbe871994b6fb4f486108055f0cb05255e2b169327e45d3eb21517cca22d03\",\"hash\":\"0000000000000000000aea5de9e6bfe679dc1dfd04588aed76b07366121b5c48\",\"hash_le\":\"485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000\",\"height\":606211,\"prevhash\":\"00000000000000000004669f8082b56686b42523e58b1a8ce3f14830f38f5148\",\"prevhash_le\":\"48518ff33048f1e38c1a8be52325b48666b582809f6604000000000000000000\",\"merkle_root\":\"162b5e2505cbf055801086f4b46f4b9971e8cbbf950d8d7fb92f26d9ff41cbb6\",\"merkle_root_le\":\"b6cb41ffd9262fb97f8d0d95bfcbe871994b6fb4f486108055f0cb05255e2b16\"},{\"raw\":\"00000020485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000dd69fee2152de8dcf03130c9f0f7a275a8b1bf505f2f48220ce29f9808c4e3bc562ce45d3eb215177a849ca7\",\"hash\":\"000000000000000000026cc286ad60435e8990a68061c9337488d8a8b9ea6e81\",\"hash_le\":\"816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000\",\"height\":606212,\"prevhash\":\"0000000000000000000aea5de9e6bfe679dc1dfd04588aed76b07366121b5c48\",\"prevhash_le\":\"485c1b126673b076ed8a5804fd1ddc79e6bfe6e95dea0a000000000000000000\",\"merkle_root\":\"bce3c408989fe20c22482f5f50bfb1a875a2f7f0c93031f0dce82d15e2fe69dd\",\"merkle_root_le\":\"dd69fee2152de8dcf03130c9f0f7a275a8b1bf505f2f48220ce29f9808c4e3bc\"},{\"raw\":\"00e00020816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000af66c3078533b17cc844baccc1a0da793fee0cea88e5ce3ab98775ca4de040c6002ee45d3eb2151723106eb5\",\"hash\":\"0000000000000000000d8e1f3505cb971edcd2e8c1d832cd6363f42b8e823f58\",\"hash_le\":\"583f828e2bf46363cd32d8c1e8d2dc1e97cb05351f8e0d000000000000000000\",\"height\":606213,\"prevhash\":\"000000000000000000026cc286ad60435e8990a68061c9337488d8a8b9ea6e81\",\"prevhash_le\":\"816eeab9a8d8887433c96180a690895e4360ad86c26c02000000000000000000\",\"merkle_root\":\"c640e04dca7587b93acee588ea0cee3f79daa0c1ccba44c87cb1338507c366af\",\"merkle_root_le\":\"af66c3078533b17cc844baccc1a0da793fee0cea88e5ce3ab98775ca4de040c6\"}]" - func getGenesisHeaders() (BitcoinHeader, []BitcoinHeader) { + headerJSON, jsonErr := ioutil.ReadFile("scripts/json_data/genesis.json") + if jsonErr != nil { + panic("could not retreive data in gen_state: " + jsonErr.Error()) + } + var genesisHeaders []BitcoinHeader err := json.Unmarshal([]byte(headerJSON), &genesisHeaders) if err != nil { - panic("bad json in gen_state") + panic("bad json in gen_state: " + err.Error()) } return genesisHeaders[0], genesisHeaders[1:] } diff --git a/golang/x/relay/genesis.go b/golang/x/relay/genesis.go index d522da3f..b0dd00fa 100644 --- a/golang/x/relay/genesis.go +++ b/golang/x/relay/genesis.go @@ -35,6 +35,7 @@ func ValidateGenesis(data GenesisState) error { return err } + // Genesis state must include first block of an epoch plus another block belonging to that same epoch if data.PeriodStart.Height != (data.Headers[0].Height - (data.Headers[0].Height % 2016)) { return errors.New("period start has incorrect height") } @@ -57,9 +58,11 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.Valid if err != nil { panic("already init!") } - err = keeper.IngestHeaderChain(ctx, data.Headers[1:]) - if err != nil { - panic("Bad header chain in genesis state!") + if len(data.Headers) > 1 { + err = keeper.IngestHeaderChain(ctx, data.Headers[1:]) + if err != nil { + panic("Bad header chain in genesis state! " + err.Error()) + } } return []abci.ValidatorUpdate{} } diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 4bf5d607..51f75f48 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -118,7 +118,6 @@ func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, new 3. Both are in the same window, choose the higher one 4. They're in different new windows. Choose the heavier one */ - if !leftInPeriod && rightInPeriod { return leftBlock.HashLE, nil } diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 7675f4ce..3f340cdc 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -27,8 +27,19 @@ func (k Keeper) hasRequest(ctx sdk.Context, id types.RequestID) bool { func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValue uint64, numConfs uint8, origin types.Origin, action types.HexBytes) sdk.Error { store := k.getRequestStore(ctx) - spendsDigest := btcspv.Hash256(spends) - paysDigest := btcspv.Hash256(pays) + var spendsDigest types.Hash256Digest + if len(spends) == 0 { + spendsDigest = types.Hash256Digest{} + } else { + spendsDigest = btcspv.Hash256(spends) + } + + var paysDigest types.Hash256Digest + if len(spends) == 0 { + paysDigest = types.Hash256Digest{} + } else { + paysDigest = btcspv.Hash256(pays) + } request := types.ProofRequest{ Spends: spendsDigest, @@ -147,7 +158,7 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, v return types.ErrClosedRequest(types.DefaultCodespace) } - hasPays := req.Pays != btcspv.Hash256([]byte{0}) + hasPays := req.Pays != btcspv.Hash256Digest{} if hasPays { // We can ignore this error because we know that ValidateVout passed out, _ := btcspv.ExtractOutputAtIndex(vout, uint(outputIndex)) @@ -162,7 +173,7 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, v } } - hasSpends := req.Spends != btcspv.Hash256([]byte{0}) + hasSpends := req.Spends != btcspv.Hash256Digest{} if hasSpends { in, err := btcspv.ExtractInputAtIndex(vin, uint(inputIndex)) if err != nil { diff --git a/golang/x/relay/types/codec.go b/golang/x/relay/types/codec.go index 25fe539f..bd3f33dd 100644 --- a/golang/x/relay/types/codec.go +++ b/golang/x/relay/types/codec.go @@ -17,4 +17,5 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgIngestDifficultyChange{}, "relay/IngestDifficultyChange", nil) cdc.RegisterConcrete(MsgMarkNewHeaviest{}, "relay/MarkNewHeaviest", nil) cdc.RegisterConcrete(MsgNewRequest{}, "relay/NewRequest", nil) + cdc.RegisterConcrete(MsgProvideProof{}, "relay/ProvideProof", nil) } diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index c841d4b8..7db38cd4 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -108,6 +108,10 @@ type MsgMarkNewHeaviest struct { // NewMsgMarkNewHeaviest instantiates a MsgMarkNewHeaviest func NewMsgMarkNewHeaviest(address sdk.AccAddress, ancestor Hash256Digest, currentBest RawHeader, newBest RawHeader, limit uint32) MsgMarkNewHeaviest { + if limit == 0 { + limit = DefaultLookupLimit + } + return MsgMarkNewHeaviest{ address, ancestor, @@ -147,8 +151,8 @@ func (msg MsgMarkNewHeaviest) Route() string { return RouterKey } // MsgNewRequest defines a NewRequest message type MsgNewRequest struct { Signer sdk.AccAddress `json:"signer"` - Spends []byte `json:"spends"` - Pays []byte `json:"pays"` + Spends HexBytes `json:"spends"` + Pays HexBytes `json:"pays"` PaysValue uint64 `json:"paysValue"` NumConfs uint8 `json:"numConfs"` Origin Origin `json:"origin"` @@ -179,7 +183,7 @@ func (msg MsgNewRequest) Type() string { return "new_request" } // ValidateBasic runs stateless validation func (msg MsgNewRequest) ValidateBasic() sdk.Error { // TODO: validate output types - if len(msg.Spends) != 36 { + if len(msg.Spends) != 36 && len(msg.Spends) != 0 { return ErrSpendsLength(DefaultCodespace) } if len(msg.Pays) > 50 { diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index b2618676..3e0b3f0e 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -31,7 +31,7 @@ const ( // QueryGetRequest is a query string tag for getRequest QueryGetRequest = "getrequest" - // QueryCheckRequest is a query string tag for checkRequests + // QueryCheckRequests is a query string tag for checkRequests QueryCheckRequests = "checkrequests" // QueryCheckProof is a query string tag for checkProof From 244612254f5d9a93cf81e6cbdc068ef2e6d18149 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Wed, 1 Apr 2020 14:16:09 -0700 Subject: [PATCH 069/148] feature: getBestKnownDigest query (#54) --- golang/README.md | 3 +-- golang/x/relay/alias.go | 2 +- golang/x/relay/client/cli/query.go | 26 +++++++++++++++++++++++++- golang/x/relay/client/rest/query.go | 13 +++++++++++++ golang/x/relay/client/rest/rest.go | 1 + golang/x/relay/client/rest/tx.go | 5 +++++ golang/x/relay/keeper/querier.go | 22 ++++++++++++++++++++++ golang/x/relay/types/querier.go | 15 +++++++++++++++ 8 files changed, 83 insertions(+), 4 deletions(-) diff --git a/golang/README.md b/golang/README.md index bb2c7f2e..18ead7c3 100644 --- a/golang/README.md +++ b/golang/README.md @@ -42,13 +42,12 @@ ## How to add a view function (queries) 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` 1. Add response type to `x/relay/types/querier.go` + 1. Add new string tag for the new query 1. Response type is a struct with the return values 1. Implement `String()` for the response type 1. Add function to querier `x/relay/keeper/querier.go` - 1. Add new string tag for the new query 1. Add new `query___` function 1. Add new case block to `switch` in `NewQuerier()` -1. Add alias type in `x/relay/alias.go` 1. Add to CLI 1. add to `x/relay/client/cli/query.go` 1. `func GetCmd______` diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index 255f16f4..aee17ded 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -25,7 +25,7 @@ var ( NewMsgIngestDifficultyChange = types.NewMsgIngestDifficultyChange // NewMsgMarkNewHeaviest is what is says on the tin NewMsgMarkNewHeaviest = types.NewMsgMarkNewHeaviest - // NewMsgGetRequest is what is says on the tin + // NewMsgNewRequest is what is says on the tin NewMsgNewRequest = types.NewMsgNewRequest // NewMsgProvideProof is what is says on the tin NewMsgProvideProof = types.NewMsgProvideProof diff --git a/golang/x/relay/client/cli/query.go b/golang/x/relay/client/cli/query.go index e911426d..cf104316 100644 --- a/golang/x/relay/client/cli/query.go +++ b/golang/x/relay/client/cli/query.go @@ -26,6 +26,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command { GetCmdIsAncestor(queryRoute, cdc), GetCmdGetRelayGenesis(queryRoute, cdc), GetCmdGetLastReorgLCA(queryRoute, cdc), + GetCmdGetBestDigest(queryRoute, cdc), GetCmdFindAncestor(queryRoute, cdc), GetCmdIsMostRecentCommonAncestor(queryRoute, cdc), GetCmdHeaviestFromAncestor(queryRoute, cdc), @@ -126,7 +127,7 @@ func GetCmdGetLastReorgLCA(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "getlastreorglca", Example: "getlastreorglca", - Long: "Returns the best known digest in the relay", + Long: "Returns the latest common ancestor of the last-known reorg", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) @@ -144,6 +145,29 @@ func GetCmdGetLastReorgLCA(queryRoute string, cdc *codec.Codec) *cobra.Command { } } +// GetCmdGetBestDigest returns the CLI command struct for GetBestDigest +func GetCmdGetBestDigest(queryRoute string, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "getbestdigest", + Example: "getbestdigest", + Long: "Returns the best known digest in the relay", + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, _, err := cliCtx.QueryWithData("custom/relay/getbestdigest", nil) + + if err != nil { + fmt.Println("could not get best known digest") + return nil + } + + var out types.QueryResGetBestDigest + cdc.MustUnmarshalJSON(res, &out) + return cliCtx.PrintOutput(&out) + }, + } +} + // GetCmdFindAncestor returns the CLI command struct for FindAncestor func GetCmdFindAncestor(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ diff --git a/golang/x/relay/client/rest/query.go b/golang/x/relay/client/rest/query.go index 9ebfd72f..18a89dbe 100644 --- a/golang/x/relay/client/rest/query.go +++ b/golang/x/relay/client/rest/query.go @@ -91,6 +91,19 @@ func getLastReorgLCAHandler(cliCtx context.CLIContext, storeName string) http.Ha } } +// handler function for getBestDigest queries +func getBestDigest(cliCtx context.CLIContext, storeName string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + res, _, err := cliCtx.QueryWithData("custom/relay/getbestdigest", nil) + if err != nil { + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + // handler function for findAncestor queries. parses arguments from url string, and passes them through // as a QueryParamsFindAncestor struct func findAncestorHandler(cliCtx context.CLIContext, storeName string) http.HandlerFunc { diff --git a/golang/x/relay/client/rest/rest.go b/golang/x/relay/client/rest/rest.go index 3d3e0e45..8806d080 100644 --- a/golang/x/relay/client/rest/rest.go +++ b/golang/x/relay/client/rest/rest.go @@ -26,6 +26,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, storeName string) s.HandleFunc("/isancestor/{digest}/{ancestor}/{limit}", isAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/getrelaygenesis", getRelayGenesisHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/getlastreorglca", getLastReorgLCAHandler(cliCtx, storeName)).Methods("GET") + s.HandleFunc("/getbestdigest", getBestDigest(cliCtx, storeName)).Methods("GET") s.HandleFunc("/findancestor/{digest}/{offset}", findAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/ismostrecentcommonancestor/{ancestor}/{left}/{right}/", isMostRecentCommonAncestorHandler(cliCtx, storeName)).Methods("GET") s.HandleFunc("/ismostrecentcommonancestor/{ancestor}/{left}/{right}/{limit}", isMostRecentCommonAncestorHandler(cliCtx, storeName)).Methods("GET") diff --git a/golang/x/relay/client/rest/tx.go b/golang/x/relay/client/rest/tx.go index 5db2774a..de6f7aa9 100644 --- a/golang/x/relay/client/rest/tx.go +++ b/golang/x/relay/client/rest/tx.go @@ -11,6 +11,7 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) +// IngestHeaderChainReq is the request struct for ingest header chain type IngestHeaderChainReq struct { BaseReq rest.BaseReq `json:"base_req"` Headers []types.BitcoinHeader `json:"headers"` @@ -48,6 +49,7 @@ func ingestHeaderChainHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +// IngestDifficultyChangeReq is the request struct for ingest difficulty change type IngestDifficultyChangeReq struct { BaseReq rest.BaseReq `json:"base_req"` Start types.Hash256Digest `json:"prevEpochStart"` @@ -86,6 +88,7 @@ func ingestDifficultyChangeHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +// MarkNewHeaviestReq is the request struct for mark new heaviest type MarkNewHeaviestReq struct { BaseReq rest.BaseReq `json:"base_req"` Ancestor types.Hash256Digest `json:"ancestor"` @@ -126,6 +129,7 @@ func markNewHeaviestHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +// NewRequestReq is the request struct for a new proof request type NewRequestReq struct { BaseReq rest.BaseReq `json:"base_req"` Spends []byte `json:"spends"` @@ -166,6 +170,7 @@ func newRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +// ProvideProofReq is the request struct for a new provide proof message type ProvideProofReq struct { BaseReq rest.BaseReq `json:"base_req"` Proof types.SPVProof `json:"proof"` diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index b6aefffe..a95454d1 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -32,6 +32,8 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryGetRelayGenesis(ctx, req, keeper) case types.QueryGetLastReorgLCA: return queryGetLastReorgLCA(ctx, req, keeper) + case types.QueryGetBestDigest: + return queryGetBestDigest(ctx, req, keeper) case types.QueryFindAncestor: return queryFindAncestor(ctx, req, keeper) case types.QueryHeaviestFromAncestor: @@ -120,6 +122,26 @@ func queryGetLastReorgLCA(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) return res, nil } +func queryGetBestDigest(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // This calls the keeper and gets an answer + result, err := keeper.GetBestKnownDigest(ctx) + if err != nil { + return []byte{}, err + } + + // Now we format the answer as a response + response := types.QueryResGetBestDigest{ + Res: result, + } + + // And we serialize that response as JSON + res, marshalErr := codec.MarshalJSONIndent(keeper.cdc, response) + if marshalErr != nil { + return []byte{}, types.ErrMarshalJSON(types.DefaultCodespace) + } + return res, nil +} + func queryFindAncestor(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params types.QueryParamsFindAncestor diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 3e0b3f0e..9bd254e1 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -19,6 +19,9 @@ const ( // QueryGetLastReorgLCA is a query string tag for GetLastReorgLCA QueryGetLastReorgLCA = "getlastreorglca" + // QueryGetBestDigest is a query string tag for GetBestDigest + QueryGetBestDigest = "getbestdigest" + // QueryFindAncestor is a query string tag for FindAncestor QueryFindAncestor = "findancestor" @@ -82,6 +85,18 @@ func (r QueryResGetLastReorgLCA) String() string { return fmt.Sprintf("%s\n", digest) } +// QueryResGetBestDigest is the response struct for queryGetBestDigest +type QueryResGetBestDigest struct { + Res Hash256Digest `json:"result"` +} + +// String formats a QueryResGetBestDigest struct +func (r QueryResGetBestDigest) String() string { + digest := "0x" + hex.EncodeToString(r.Res[:]) + return fmt.Sprintf("%s\n", digest) +} + + // QueryParamsFindAncestor represents the parameters for a FindAncestor query type QueryParamsFindAncestor struct { DigestLE Hash256Digest `json:"digestLE"` From 764ad4a417638660afb410284373565cfa7be7f4 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Wed, 1 Apr 2020 15:29:26 -0700 Subject: [PATCH 070/148] tests: fix tests I broke in a previous PR (#55) --- golang/x/relay/keeper/request_test.go | 2 +- golang/x/relay/keeper/requests.go | 2 +- golang/x/relay/keeper/validator_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go index 79ce9e82..23949461 100644 --- a/golang/x/relay/keeper/request_test.go +++ b/golang/x/relay/keeper/request_test.go @@ -146,7 +146,7 @@ func (s *KeeperSuite) TestCheckRequests() { s.Equal(sdk.CodeType(608), err.Code()) // Errors if input value does not equal spends value - requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{0}, 0, 255, types.Local, nil) + requestErr = s.Keeper.setRequest(s.Context, []byte{1}, []byte{}, 0, 255, types.Local, nil) s.SDKNil(requestErr) err = s.Keeper.checkRequests( s.Context, diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 3f340cdc..2727c415 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -35,7 +35,7 @@ func (k Keeper) setRequest(ctx sdk.Context, spends []byte, pays []byte, paysValu } var paysDigest types.Hash256Digest - if len(spends) == 0 { + if len(pays) == 0 { paysDigest = types.Hash256Digest{} } else { paysDigest = btcspv.Hash256(pays) diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go index 241dada2..7ea118bf 100644 --- a/golang/x/relay/keeper/validator_test.go +++ b/golang/x/relay/keeper/validator_test.go @@ -67,7 +67,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { s.Keeper.ingestHeader(s.Context, validProof.Proof.ConfirmingHeader) s.Keeper.setLink(s.Context, validProof.Proof.ConfirmingHeader) s.Keeper.ingestHeader(s.Context, validProof.BestKnown) - requestErr := s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 4, types.Local, nil) + requestErr := s.Keeper.setRequest(s.Context, []byte{}, []byte{}, 0, 4, types.Local, nil) s.Nil(requestErr) // errors if getConfs fails From 7d9c48320b05175991107decbb97ea5bfa459cfe Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Thu, 2 Apr 2020 17:42:06 -0400 Subject: [PATCH 071/148] Makefile command for initializing chain (#53) --- golang/Makefile | 3 +++ golang/scripts/README.md | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/golang/Makefile b/golang/Makefile index eaac1b60..97a095fb 100644 --- a/golang/Makefile +++ b/golang/Makefile @@ -28,3 +28,6 @@ lint: test: @go test -mod=readonly $(PACKAGES) + +init: + ./scripts/init_chain.sh diff --git a/golang/scripts/README.md b/golang/scripts/README.md index 75a77fa8..afd0b060 100644 --- a/golang/scripts/README.md +++ b/golang/scripts/README.md @@ -25,14 +25,14 @@ relayd help relaycli help ``` ## Running the CLI -To run the CLI for manual testing, you can run `./scripts/init_chain.sh` to initialize a new chain.

+To run the CLI for manual testing, you can run `make init` to initialize a new chain.

All chain related data lives in `scripts/json_data`. Edit `scripts/json_data/genesis.json` to generate a customized genesis state. This JSON must be a list of block headers pertaining to one epoch. The first header must be the first block of the epoch. The remaining headers must be ordered headers beginning at any height in the epoch. ```bash # Set the executable rights if not done already chmod +x scripts/init_chain.sh -#run script -./scripts/init_chain.sh +# initialize chain with data from scripts/json_data/genesis.json +make init ``` Open up a new terminal tab in the same directory to begin interacting with the chain. As per the setup script, you can now interact via username/password `me / 12345678` such that when submitting transactions using flag `--from me` when prompted for the password enter: `12345678` From 2ffe884c982a65487c353797edffd6e4d9fb9a0d Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Fri, 3 Apr 2020 15:49:47 -0700 Subject: [PATCH 072/148] feature: #48 add a ProofProvided event (#56) --- golang/x/relay/keeper/validator.go | 13 +++++++++++++ golang/x/relay/types/events.go | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go index 42c5100c..43ef3eb3 100644 --- a/golang/x/relay/keeper/validator.go +++ b/golang/x/relay/keeper/validator.go @@ -5,6 +5,17 @@ import ( "github.com/summa-tx/relays/golang/x/relay/types" ) +func (k Keeper) emitProofProvided( + ctx sdk.Context, + filled types.FilledRequests, +) { + filledIDs := []types.RequestID{} + for _, f := range filled.Filled { + filledIDs = append(filledIDs, f.ID) + } + ctx.EventManager().EmitEvent(types.NewProofProvidedEvent(filled.Proof.TxIDLE, filledIDs)) +} + func (k Keeper) getConfs(ctx sdk.Context, header types.BitcoinHeader) (uint32, sdk.Error) { bestKnown, err := k.GetBestKnownDigest(ctx) if err != nil { @@ -71,5 +82,7 @@ func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.Filled return err } } + + k.emitProofProvided(ctx, filledRequests) return nil } diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index 10f6be93..a3eab88b 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -1,6 +1,7 @@ package types import ( + "encoding/json" "encoding/hex" "fmt" @@ -12,6 +13,7 @@ const ( EventTypeExtension = "extension" EventTypeReorg = "reorg" EventTypeProofRequest = "proof_request" + EventTypeProofProvided = "proof_provided" AttributeKeyFirstBlock = "first_block" AttributeKeyLastBlock = "last_block" @@ -25,6 +27,9 @@ const ( AttributeKeySpends = "spends" AttributeKeyPaysValue = "value" AttributeKeyOrigin = "origin" + + AttributeKeyTXID = "txid" + AttributeKeyFilled = "filled" ) // NewReorgEvent instantiates a reorg event @@ -57,3 +62,13 @@ func NewProofRequestEvent(pays, spends []byte, paysValue uint64, id RequestID, o sdk.NewAttribute(AttributeKeyOrigin, fmt.Sprintf("%d", origin)), ) } + +// NewProofProvidedEvent instantiates a proof provided event +func NewProofProvidedEvent(txid Hash256Digest, filled []RequestID) sdk.Event { + filledJSON, _ := json.Marshal(filled) + return sdk.NewEvent( + EventTypeProofProvided, + sdk.NewAttribute(AttributeKeyTXID, "0x" + hex.EncodeToString(txid[:])), + sdk.NewAttribute(AttributeKeyFilled, string(filledJSON)), + ) +} From 75e51a08c8ccb3ee6874c4d8c4beaf76cd6513c3 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 7 Apr 2020 13:39:39 -0700 Subject: [PATCH 073/148] version: bump solidity to v2.0.1 --- solidity/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solidity/package.json b/solidity/package.json index 0595f55e..4a295ae0 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,6 +1,6 @@ { "name": "@summa-tx/relay-sol", - "version": "2.0.0", + "version": "2.0.1", "description": "Bitcoin Relay", "main": "index.js", "scripts": { From 63ed2084c65d0c0e9e4424793d466c445a872efa Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 7 Apr 2020 17:14:17 -0700 Subject: [PATCH 074/148] feature: alfajores migrations --- solidity/migrations/networkInfo.js | 7 ++++++- solidity/truffle-config.js | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index 332cc593..6e721056 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -37,5 +37,10 @@ module.exports = { network_id: truffleConf.networks.kovan_test.network_id, bitcoin: bitcoinTest, firstID: ID_SPACE_SIZE.muln(truffleConf.networks.kovan_test.network_id) - } + }, + alfajores_test: { + network_id: truffleConf.networks.alfajores_test.network_id, + bitcoin: bitcoinTest, + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id) + }, }; diff --git a/solidity/truffle-config.js b/solidity/truffle-config.js index 6b8ab0fd..9e408300 100644 --- a/solidity/truffle-config.js +++ b/solidity/truffle-config.js @@ -20,6 +20,16 @@ const kovan = { timeoutBlocks: 200 } +const alfajores = { + host: process.env.ALFAJORES_NODE_URL, + port: 8545, // Standard Ethereum port (default: none) + network_id: 44786, + port: process.env.ALFAJORES_NODE_PORT, + from: process.env.ALFAJORES_FROM, + gas: 8000000, + gasPrice: 100000000000 +} + module.exports = { api_keys: { etherscan: process.env.ETHERSCAN_KEY @@ -41,6 +51,9 @@ module.exports = { kovan: kovan, kovan_test: kovan, + + alfajores: alfajores, + alfajores_test: alfajores, }, // mocha: { From 4939e3e48c3abc9b09fa516a004b97542a7b3b8c Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 7 Apr 2020 17:26:38 -0700 Subject: [PATCH 075/148] bug: fix imports in maintainer --- maintainer/base.py | 2 +- maintainer/bitcoin/bcoin_rpc.py | 4 ++-- maintainer/bitcoin/bsock.py | 2 +- maintainer/config/__init__.py | 2 +- maintainer/ethereum/contract.py | 6 +++--- maintainer/ethereum/shared.py | 2 +- maintainer/header_forwarder/h.py | 8 ++++---- maintainer/header_forwarder/pull.py | 2 +- maintainer/header_forwarder/push.py | 8 ++++---- maintainer/relay_types.py | 1 - maintainer/utils.py | 2 +- solidity/migrations/networkInfo.js | 5 +++++ 12 files changed, 24 insertions(+), 20 deletions(-) diff --git a/maintainer/base.py b/maintainer/base.py index c60822ed..043463bf 100644 --- a/maintainer/base.py +++ b/maintainer/base.py @@ -6,7 +6,7 @@ from functools import partial from dotenv import load_dotenv -from relay import config +from maintainer import config from typing import Awaitable, Callable from asyncio.events import AbstractEventLoop diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/bitcoin/bcoin_rpc.py index d681e4a7..bb1cfc40 100644 --- a/maintainer/bitcoin/bcoin_rpc.py +++ b/maintainer/bitcoin/bcoin_rpc.py @@ -1,9 +1,9 @@ import aiohttp import logging -from relay import config +from maintainer import config -from relay.relay_types import BCoinTx +from maintainer.relay_types import BCoinTx from btcspv.types import RelayHeader from typing import Any, cast, Dict, List, Optional, Tuple, Union S = aiohttp.ClientSession diff --git a/maintainer/bitcoin/bsock.py b/maintainer/bitcoin/bsock.py index c9ab96d8..8f393591 100644 --- a/maintainer/bitcoin/bsock.py +++ b/maintainer/bitcoin/bsock.py @@ -2,7 +2,7 @@ import logging import socketio -from relay import config +from maintainer import config from typing import Dict, List, Tuple, Union diff --git a/maintainer/config/__init__.py b/maintainer/config/__init__.py index 7bc939ae..96bd6344 100644 --- a/maintainer/config/__init__.py +++ b/maintainer/config/__init__.py @@ -3,7 +3,7 @@ from ether import crypto from typing import cast, Tuple, Optional -from relay.relay_types import RelayConfig +from maintainer.relay_types import RelayConfig CONFIG: RelayConfig diff --git a/maintainer/ethereum/contract.py b/maintainer/ethereum/contract.py index 0ea4ef4a..52d16c65 100644 --- a/maintainer/ethereum/contract.py +++ b/maintainer/ethereum/contract.py @@ -1,9 +1,9 @@ import logging from ether import abi, calldata, events -from relay import config -from relay.ethereum import shared -from relay.relay_abi import ABI as relay_ABI +from maintainer import config +from maintainer.ethereum import shared +from maintainer.relay_abi import ABI as relay_ABI EXPIRED = events._make_topic0( abi.find('SubscriptionExpired', relay_ABI)[0]) diff --git a/maintainer/ethereum/shared.py b/maintainer/ethereum/shared.py index 2abaeba8..2cb7fb25 100644 --- a/maintainer/ethereum/shared.py +++ b/maintainer/ethereum/shared.py @@ -3,7 +3,7 @@ from ether import calldata, ethrpc -from relay import config +from maintainer import config from ether.ether_types import Receipt from ether.transactions import UnsignedEthTx diff --git a/maintainer/header_forwarder/h.py b/maintainer/header_forwarder/h.py index e08a39da..94cc9c44 100644 --- a/maintainer/header_forwarder/h.py +++ b/maintainer/header_forwarder/h.py @@ -2,10 +2,10 @@ import asyncio import logging -from relay import base, utils -from relay.bitcoin import bcoin_rpc, bsock -from relay.ethereum import contract, shared -from relay.header_forwarder import pull, push +from maintainer import base, utils +from maintainer.bitcoin import bcoin_rpc, bsock +from maintainer.ethereum import contract, shared +from maintainer.header_forwarder import pull, push from typing import cast from btcspv.types import RelayHeader diff --git a/maintainer/header_forwarder/pull.py b/maintainer/header_forwarder/pull.py index e1d24f61..4ef76368 100644 --- a/maintainer/header_forwarder/pull.py +++ b/maintainer/header_forwarder/pull.py @@ -1,7 +1,7 @@ import asyncio import logging -from relay.bitcoin import bcoin_rpc +from maintainer.bitcoin import bcoin_rpc from typing import cast from btcspv.types import RelayHeader diff --git a/maintainer/header_forwarder/push.py b/maintainer/header_forwarder/push.py index f18f31e6..ef9ae031 100644 --- a/maintainer/header_forwarder/push.py +++ b/maintainer/header_forwarder/push.py @@ -1,10 +1,10 @@ import asyncio import logging -from relay import config, utils -from relay.bitcoin import bcoin_rpc -from relay.ethereum import contract, shared -from relay.relay_abi import ABI as relay_ABI +from maintainer import config, utils +from maintainer.bitcoin import bcoin_rpc +from maintainer.ethereum import contract, shared +from maintainer.relay_abi import ABI as relay_ABI from typing import cast, List from btcspv.types import RelayHeader diff --git a/maintainer/relay_types.py b/maintainer/relay_types.py index fc51e2fc..55a0ba1f 100644 --- a/maintainer/relay_types.py +++ b/maintainer/relay_types.py @@ -72,6 +72,5 @@ class RelayConfig(TypedDict): GETH_UNLOCK: Optional[str] BCOIN_URL: str BCOIN_WS_URL: str - PLUGIN_WS_URL: str PROJECT_ID: str CONTRACT: str diff --git a/maintainer/utils.py b/maintainer/utils.py index 56d5d070..4e13f2a1 100644 --- a/maintainer/utils.py +++ b/maintainer/utils.py @@ -1,6 +1,6 @@ from riemann import tx from btcspv.types import RelayHeader -from relay.relay_types import RelayRequest +from maintainer.relay_types import RelayRequest def format_header(h: RelayHeader) -> str: diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index 6e721056..a5b19152 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -38,6 +38,11 @@ module.exports = { bitcoin: bitcoinTest, firstID: ID_SPACE_SIZE.muln(truffleConf.networks.kovan_test.network_id) }, + alfajores: { + network_id: truffleConf.networks.alfajores_test.network_id, + bitcoin: bitcoinTest, + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id) + }, alfajores_test: { network_id: truffleConf.networks.alfajores_test.network_id, bitcoin: bitcoinTest, From 7cd0939003e30bf8518e872a5d03d116c995f3f9 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 7 Apr 2020 17:30:10 -0700 Subject: [PATCH 076/148] bug:add missing setup.py --- setup.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..535129ac --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +from setuptools import setup, find_packages + +setup( + name='summa-relay', + version='0.1.0', + description=('Summa minimal relay'), + author=["James Prestwich"], + license="LGPLv3.0", + install_requires=[ + 'aiohttp', + 'riemann-ether', + 'riemann-keys', + 'mypy-extensions'], + packages=find_packages(), + package_data={'relay': ['py.typed']}, + package_dir={'relay': 'relay'}, + python_requires='>=3.6' +) From 89864d13d75d072b693f2e0fa69eeeee8bff4fb9 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Tue, 7 Apr 2020 20:13:46 -0700 Subject: [PATCH 077/148] bug: fix migration for alfajores always using testnet --- solidity/migrations/networkInfo.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index a5b19152..188f09cc 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -26,7 +26,7 @@ module.exports = { ropsten_test: { network_id: truffleConf.networks.ropsten_test.network_id, bitcoin: bitcoinTest, - firstID: ID_SPACE_SIZE.muln(truffleConf.networks.ropsten_test.network_id) + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.ropsten_test.network_id + 0x800000) }, kovan: { network_id: truffleConf.networks.kovan.network_id, @@ -36,16 +36,16 @@ module.exports = { kovan_test: { network_id: truffleConf.networks.kovan_test.network_id, bitcoin: bitcoinTest, - firstID: ID_SPACE_SIZE.muln(truffleConf.networks.kovan_test.network_id) + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.kovan_test.network_id + 0x800000) }, alfajores: { network_id: truffleConf.networks.alfajores_test.network_id, - bitcoin: bitcoinTest, - firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id) + bitcoin: bitcoinMain, + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores.network_id) }, alfajores_test: { network_id: truffleConf.networks.alfajores_test.network_id, bitcoin: bitcoinTest, - firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id) + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id + 0x800000) }, }; From abc4163140d9ac71210dce01e22e9fdff2e8e925 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 12:13:06 -0700 Subject: [PATCH 078/148] feature: hint on bad header forwarder setup --- Pipfile | 2 +- Pipfile.lock | 14 +++++++------- maintainer/header_forwarder/h.py | 10 +++++++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Pipfile b/Pipfile index 631f2864..417a96d4 100644 --- a/Pipfile +++ b/Pipfile @@ -16,7 +16,7 @@ pytest-cov = "*" atomicwrites = "*" [packages] -riemann-ether = "==6.0.5" +riemann-ether = "==6.0.6" aiohttp = "*" riemann-keys = "*" mypy-extensions = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 0561b872..7f70b8ec 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "10b48b44e058d0a64ad4d277d1fe5bbbfab26336458c286416739ef10737a140" + "sha256": "da94996eb6aeaa92d8861fe59c46b3be43326d39e2d4d73c6c79ebf7bd3a795a" }, "pipfile-spec": 6, "requires": { @@ -239,11 +239,11 @@ }, "riemann-ether": { "hashes": [ - "sha256:4b40264b3e53c88a66939e18f082845d4ce931860008d69e43939b30ebd7b07d", - "sha256:bccf83a246bc000f41eccfcb4cfd4750196dafc8395275017440e00572ea0f2f" + "sha256:105c4ed41c6dc5f345cd9a7d18a8591e19467893a732ad187d6568d8d1f721eb", + "sha256:4f777cc2be070521e9e47106aa3eba0b6f8cbe19cd1ccc5e7cf0ee04d2398af9" ], "index": "pypi", - "version": "==6.0.5" + "version": "==6.0.6" }, "riemann-keys": { "hashes": [ @@ -565,10 +565,10 @@ }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "version": "==2.4.6" + "version": "==2.4.7" }, "pytest": { "hashes": [ diff --git a/maintainer/header_forwarder/h.py b/maintainer/header_forwarder/h.py index 94cc9c44..5a91b4af 100644 --- a/maintainer/header_forwarder/h.py +++ b/maintainer/header_forwarder/h.py @@ -29,9 +29,13 @@ async def run() -> None: f'Received {len(latest_digest) // 2} bytes instead. ' 'Hint: is this account authorized?') - latest = cast( - RelayHeader, - await bcoin_rpc.get_header_by_hash(latest_digest)) + latest_or_none = await bcoin_rpc.get_header_by_hash(latest_digest) + if latest_or_none is None: + raise ValueError( + 'Relay\'s latest digest is not known to the Bitcoin node. ' + f'Got {latest_digest}. ' + 'Hint: is your node on the same Bitcoin network as the relay?') + latest = cast(RelayHeader, latest_or_none) better_or_same = cast( RelayHeader, await bcoin_rpc.get_header_by_height(latest['height'])) From a7c627b3c06b42e8bad6f3192cba08726b2e7fc7 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:14:25 -0700 Subject: [PATCH 079/148] dep: bump bitcoin-spv-py to 3.0.1 --- Pipfile | 2 +- Pipfile.lock | 8 ++++---- maintainer/bitcoin/bcoin_rpc.py | 9 +++------ maintainer/header_forwarder/push.py | 11 ++++++----- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Pipfile b/Pipfile index 417a96d4..20b8a689 100644 --- a/Pipfile +++ b/Pipfile @@ -23,7 +23,7 @@ mypy-extensions = "*" python-dotenv = "*" python-socketio = {extras = ["asyncio_client"],version = "*"} riemann-tx = "==2.1.0" -bitcoin-spv-py = "==2.0.0" +bitcoin-spv-py = "==3.0.1" python-engineio = "==3.10.0" [requires] diff --git a/Pipfile.lock b/Pipfile.lock index 7f70b8ec..9041f2bc 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "da94996eb6aeaa92d8861fe59c46b3be43326d39e2d4d73c6c79ebf7bd3a795a" + "sha256": "e7da5761168c4f0dc6b2b3063e4b9c5515bf3aed2e23f940d057d1ddbf4209c9" }, "pipfile-spec": 6, "requires": { @@ -50,11 +50,11 @@ }, "bitcoin-spv-py": { "hashes": [ - "sha256:1e2240b33e5ea26ce8bc3919495914d44f548e16a2a99aba59ff8ab62ce086d1", - "sha256:5e53e1f1f14c95e42f870e826b727030053cc1ec8059e5419f3ab58be8c84e88" + "sha256:a0c3e7047505f958ae820f094982f7f5fb52323fb5c220820435b0465899205f", + "sha256:eff76d4c6931c12c2eb082dc2c32eeef1f339c50476a24086ca9d67e15c010b2" ], "index": "pypi", - "version": "==2.0.0" + "version": "==3.0.1" }, "cffi": { "hashes": [ diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/bitcoin/bcoin_rpc.py index bb1cfc40..bbd60364 100644 --- a/maintainer/bitcoin/bcoin_rpc.py +++ b/maintainer/bitcoin/bcoin_rpc.py @@ -124,13 +124,10 @@ async def get_header_by_hash( return RelayHeader( raw=bytes.fromhex(raw)[:80], - hash=digest, - hash_le=digest[::-1], + hash=digest[::-1], height=block_info['height'], - merkle_root=merkle_root, - merkle_root_le=merkle_root[::-1], - prevhash=prevhash, - prevhash_le=prevhash[::-1]) + merkle_root=merkle_root[::-1], + prevhash=prevhash[::-1]) async def _get_header_by_height( diff --git a/maintainer/header_forwarder/push.py b/maintainer/header_forwarder/push.py index ef9ae031..5124be58 100644 --- a/maintainer/header_forwarder/push.py +++ b/maintainer/header_forwarder/push.py @@ -104,8 +104,8 @@ async def _add_diff_change(headers: List[RelayHeader]) -> None: # we know these casts won't fail old_start = cast(RelayHeader, old_start_or_none) old_end = cast(RelayHeader, old_end_or_none) - logger.debug(f'old start is {old_start["hash_le"].hex()}') - logger.debug(f'old end is {old_end["hash_le"].hex()}') + logger.debug(f'old start is {old_start["hash"].hex()}') + logger.debug(f'old end is {old_end["hash"].hex()}') headers_hex = ''.join(h['raw'].hex() for h in headers) @@ -136,20 +136,21 @@ async def _update_best_digest( await bcoin_rpc.get_header_by_hash(current_best_digest)) delta = new_best['height'] - current_best['height'] + 1 + # find the latest block in current's history that is an ancestor of new is_ancestor = False ancestor = current_best while True: is_ancestor = await contract.is_ancestor( - ancestor['hash_le'], - new_best['hash_le']) + ancestor['hash'], + new_best['hash']) if is_ancestor: break ancestor = cast( RelayHeader, await bcoin_rpc.get_header_by_hash(ancestor['prevhash'])) - ancestor_le = ancestor['hash_le'] + ancestor_le = ancestor['hash'] tx = shared.make_call_tx( contract=config.get()['CONTRACT'], From fd5ef74c7261537c77c433bb51752fcd0283519f Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:19:33 -0700 Subject: [PATCH 080/148] bug: make bcoin function accept LE hash --- maintainer/bitcoin/bcoin_rpc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/bitcoin/bcoin_rpc.py index bbd60364..fdc9cbf9 100644 --- a/maintainer/bitcoin/bcoin_rpc.py +++ b/maintainer/bitcoin/bcoin_rpc.py @@ -91,13 +91,13 @@ async def _PUT( async def get_header_by_hash( hash: Union[str, bytes], session: S = SESSION) -> Optional[RelayHeader]: - + '''Gets a header by it's LE hash''' hash_hex: str try: - hash_hex = cast(bytes, hash).hex() + hash_hex = cast(bytes, hash)[::-1].hex() except AttributeError: - hash_hex = cast(str, hash) + hash_hex = bytes.fromhex(cast(str, hash))[::-1].hex() logger.debug(f'retrieving info on {hash_hex}') payload = { From 09580070a86d8464da237700392f3fc41d62a0f8 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:23:43 -0700 Subject: [PATCH 081/148] feature: make LE/BE hash explicit in bcoin call --- maintainer/bitcoin/bcoin_rpc.py | 18 ++++++++++++++---- maintainer/header_forwarder/h.py | 4 ++-- maintainer/header_forwarder/push.py | 8 ++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/bitcoin/bcoin_rpc.py index fdc9cbf9..b2a7284c 100644 --- a/maintainer/bitcoin/bcoin_rpc.py +++ b/maintainer/bitcoin/bcoin_rpc.py @@ -88,16 +88,26 @@ async def _PUT( return status, result -async def get_header_by_hash( +async def get_header_by_hash_le( + hash: Union[str, bytes], + session: S = SESSION) -> Optional[RelayHeader]: + try: + hash_hex = cast(bytes, hash)[::-1].hex() + except AttributeError: + hash_hex = bytes.fromhex(cast(str, hash))[::-1].hex() + return await get_header_by_hash_be(hash_hex) + + +async def get_header_by_hash_be( hash: Union[str, bytes], session: S = SESSION) -> Optional[RelayHeader]: '''Gets a header by it's LE hash''' hash_hex: str try: - hash_hex = cast(bytes, hash)[::-1].hex() + hash_hex = cast(bytes, hash).hex() except AttributeError: - hash_hex = bytes.fromhex(cast(str, hash))[::-1].hex() + hash_hex = cast(str, hash) logger.debug(f'retrieving info on {hash_hex}') payload = { @@ -153,7 +163,7 @@ async def get_header_by_height( return None block_info = cast(dict, block_info_or_none) - return await get_header_by_hash(block_info['hash']) + return await get_header_by_hash_be(block_info['hash']) async def get_chain_tips(session: S = SESSION) -> List[str]: diff --git a/maintainer/header_forwarder/h.py b/maintainer/header_forwarder/h.py index 5a91b4af..c9ce69fa 100644 --- a/maintainer/header_forwarder/h.py +++ b/maintainer/header_forwarder/h.py @@ -29,7 +29,7 @@ async def run() -> None: f'Received {len(latest_digest) // 2} bytes instead. ' 'Hint: is this account authorized?') - latest_or_none = await bcoin_rpc.get_header_by_hash(latest_digest) + latest_or_none = await bcoin_rpc.get_header_by_hash_le(latest_digest) if latest_or_none is None: raise ValueError( 'Relay\'s latest digest is not known to the Bitcoin node. ' @@ -45,7 +45,7 @@ async def run() -> None: while latest != better_or_same: latest = cast( RelayHeader, - await bcoin_rpc.get_header_by_hash(latest['prevhash'])) + await bcoin_rpc.get_header_by_hash_le(latest['prevhash'])) better_or_same = cast( RelayHeader, await bcoin_rpc.get_header_by_height(latest['height'])) diff --git a/maintainer/header_forwarder/push.py b/maintainer/header_forwarder/push.py index 5124be58..a01ca644 100644 --- a/maintainer/header_forwarder/push.py +++ b/maintainer/header_forwarder/push.py @@ -74,8 +74,8 @@ async def _add_headers(headers: List[RelayHeader]) -> None: f'first is {utils.format_header(headers[0])}\n' f'last is {utils.format_header(headers[-1])}\n') nonce = next(shared.NONCE) - anchor_or_none = await bcoin_rpc.get_header_by_hash( - headers[0]['prevhash'].hex()) + anchor_or_none = await bcoin_rpc.get_header_by_hash_le( + headers[0]['prevhash']) anchor = cast(RelayHeader, anchor_or_none) headers_hex = ''.join(h['raw'].hex() for h in headers) @@ -133,7 +133,7 @@ async def _update_best_digest( current_best_digest = await contract.get_best_block() current_best = cast( RelayHeader, - await bcoin_rpc.get_header_by_hash(current_best_digest)) + await bcoin_rpc.get_header_by_hash_le(current_best_digest)) delta = new_best['height'] - current_best['height'] + 1 @@ -148,7 +148,7 @@ async def _update_best_digest( break ancestor = cast( RelayHeader, - await bcoin_rpc.get_header_by_hash(ancestor['prevhash'])) + await bcoin_rpc.get_header_by_hash_le(ancestor['prevhash'])) ancestor_le = ancestor['hash'] From c2a89f7279b2b89d9db46d34ab04740d9d468cd9 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:32:47 -0700 Subject: [PATCH 082/148] refactor: move towards standardizing on LE --- maintainer/bitcoin/bcoin_rpc.py | 1 + maintainer/ethereum/contract.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/bitcoin/bcoin_rpc.py index b2a7284c..6a94877c 100644 --- a/maintainer/bitcoin/bcoin_rpc.py +++ b/maintainer/bitcoin/bcoin_rpc.py @@ -91,6 +91,7 @@ async def _PUT( async def get_header_by_hash_le( hash: Union[str, bytes], session: S = SESSION) -> Optional[RelayHeader]: + hash_hex: str try: hash_hex = cast(bytes, hash)[::-1].hex() except AttributeError: diff --git a/maintainer/ethereum/contract.py b/maintainer/ethereum/contract.py index 52d16c65..8b37df4d 100644 --- a/maintainer/ethereum/contract.py +++ b/maintainer/ethereum/contract.py @@ -71,6 +71,8 @@ async def get_best_block() -> str: Get the contract's marked best known digest. Counterintuitively, the contract may know of a better digest that hasn't been marked yet + + returns LE digest ''' f = abi.find('getBestKnownDigest', relay_ABI)[0] selector = calldata.make_selector(f) @@ -85,5 +87,4 @@ async def get_best_block() -> str: 'latest' # block height parameter ] ) - digest = bytes.fromhex(res[2:])[::-1].hex() # block-explorer format - return digest + return res[2:] # block-explorer format From c2e0e7135bc8c9f02da5bcbbff262f010dca69f8 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:43:26 -0700 Subject: [PATCH 083/148] chore: new ABI version --- maintainer/relay_abi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/relay_abi.py b/maintainer/relay_abi.py index d4bf9536..cfeb6475 100644 --- a/maintainer/relay_abi.py +++ b/maintainer/relay_abi.py @@ -5,4 +5,4 @@ null = None -ABI = [{"constant":true,"inputs":[],"name":"getSubRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_subscriber","type":"address"}],"name":"getSubscription","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentEpochDifficulty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"seizeControl","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"}],"name":"withdrawFees","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getBestKnownDigest","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextID","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"latestValidatedTx","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPrevEpochDifficulty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_left","type":"bytes32"},{"name":"_right","type":"bytes32"},{"name":"_limit","type":"uint256"}],"name":"isMostRecentAncestor","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_digest","type":"bytes32"},{"name":"_offset","type":"uint256"}],"name":"findAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_requestID","type":"uint256"}],"name":"cancelRequest","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"PERMANENT_SUB","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"}],"name":"subscribe","outputs":[{"name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"cedeControl","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"fourWeekSub","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_digest","type":"bytes32"}],"name":"findHeight","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_anchor","type":"bytes"},{"name":"_headers","type":"bytes"}],"name":"addHeaders","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_caller","type":"address"}],"name":"isOperator","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"HEIGHT_INTERVAL","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_currentBest","type":"bytes"},{"name":"_newBest","type":"bytes"},{"name":"_limit","type":"uint256"}],"name":"markNewHeaviest","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_oldPeriodStartHeader","type":"bytes"},{"name":"_oldPeriodEndHeader","type":"bytes"},{"name":"_headers","type":"bytes"}],"name":"addHeadersWithRetarget","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ONE_DAY","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_bestKnown","type":"bytes"},{"name":"_headers","type":"bytes"}],"name":"seizeControl","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spends","type":"bytes"},{"name":"_pays","type":"bytes"},{"name":"_paysValue","type":"uint64"},{"name":"_consumer","type":"address"},{"name":"_numConfs","type":"uint8"},{"name":"_notBefore","type":"uint256"}],"name":"request","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"BASE_COST","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"remoteGasAllowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLatestValidatedTx","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"}],"name":"changePaymentAdmin","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_header","type":"bytes"},{"name":"_proof","type":"bytes"},{"name":"_version","type":"bytes4"},{"name":"_locktime","type":"bytes4"},{"name":"_index","type":"uint256"},{"name":"_reqIndices","type":"uint16"},{"name":"_vin","type":"bytes"},{"name":"_vout","type":"bytes"},{"name":"_requestID","type":"uint256"}],"name":"provideProof","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_left","type":"bytes"},{"name":"_right","type":"bytes"}],"name":"heaviestFromAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newRate","type":"uint256"}],"name":"setSubRate","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getPaymentAdmin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_beneficiary","type":"address"},{"name":"_subEnd","type":"uint256"}],"name":"manageSubscription","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_caller","type":"address"}],"name":"isSubscribed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_descendant","type":"bytes32"},{"name":"_limit","type":"uint256"}],"name":"isAncestor","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lastUpdate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLastReorgCommonAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_requestID","type":"uint256"}],"name":"getRequest","outputs":[{"name":"spends","type":"bytes32"},{"name":"pays","type":"bytes32"},{"name":"paysValue","type":"uint64"},{"name":"state","type":"uint8"},{"name":"consumer","type":"address"},{"name":"owner","type":"address"},{"name":"numConfs","type":"uint8"},{"name":"notBefore","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRelayGenesis","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOperator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FOUR_WEEKS","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_operator","type":"address"},{"name":"_paymentAdmin","type":"address"},{"name":"_genesisHeader","type":"bytes"},{"name":"_height","type":"uint256"},{"name":"_periodStart","type":"bytes32"},{"name":"_firstID","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"}],"name":"SubscriptionExpired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_first","type":"bytes32"},{"indexed":true,"name":"_last","type":"bytes32"}],"name":"Extension","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"bytes32"},{"indexed":true,"name":"_to","type":"bytes32"},{"indexed":true,"name":"_gcd","type":"bytes32"}],"name":"Reorg","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_requester","type":"address"},{"indexed":true,"name":"_requestID","type":"uint256"},{"indexed":false,"name":"_paysValue","type":"uint64"},{"indexed":false,"name":"_spends","type":"bytes"},{"indexed":false,"name":"_pays","type":"bytes"}],"name":"NewProofRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_requestID","type":"uint256"}],"name":"RequestClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_txid","type":"bytes32"},{"indexed":true,"name":"_requestID","type":"uint256"}],"name":"RequestFilled","type":"event"},{"constant":false,"inputs":[{"name":"_allowance","type":"uint256"}],"name":"setRemoteGasAllowance","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] +ABI = [{"constant":true,"inputs":[],"name":"getCurrentEpochDifficulty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBestKnownDigest","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextID","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"latestValidatedTx","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getPrevEpochDifficulty","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_left","type":"bytes32"},{"name":"_right","type":"bytes32"},{"name":"_limit","type":"uint256"}],"name":"isMostRecentAncestor","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_digest","type":"bytes32"},{"name":"_offset","type":"uint256"}],"name":"findAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_digest","type":"bytes32"}],"name":"findHeight","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_anchor","type":"bytes"},{"name":"_headers","type":"bytes"}],"name":"addHeaders","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"HEIGHT_INTERVAL","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_currentBest","type":"bytes"},{"name":"_newBest","type":"bytes"},{"name":"_limit","type":"uint256"}],"name":"markNewHeaviest","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_oldPeriodStartHeader","type":"bytes"},{"name":"_oldPeriodEndHeader","type":"bytes"},{"name":"_headers","type":"bytes"}],"name":"addHeadersWithRetarget","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"BASE_COST","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"remoteGasAllowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_left","type":"bytes"},{"name":"_right","type":"bytes"}],"name":"heaviestFromAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_ancestor","type":"bytes32"},{"name":"_descendant","type":"bytes32"},{"name":"_limit","type":"uint256"}],"name":"isAncestor","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLastReorgCommonAncestor","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRelayGenesis","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_genesisHeader","type":"bytes"},{"name":"_height","type":"uint256"},{"name":"_periodStart","type":"bytes32"},{"name":"_firstID","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_first","type":"bytes32"},{"indexed":true,"name":"_last","type":"bytes32"}],"name":"Extension","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"bytes32"},{"indexed":true,"name":"_to","type":"bytes32"},{"indexed":true,"name":"_gcd","type":"bytes32"}],"name":"Reorg","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_requester","type":"address"},{"indexed":true,"name":"_requestID","type":"uint256"},{"indexed":false,"name":"_paysValue","type":"uint64"},{"indexed":false,"name":"_spends","type":"bytes"},{"indexed":false,"name":"_pays","type":"bytes"}],"name":"NewProofRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_requestID","type":"uint256"}],"name":"RequestClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_txid","type":"bytes32"},{"indexed":true,"name":"_requestID","type":"uint256"}],"name":"RequestFilled","type":"event"},{"constant":false,"inputs":[{"name":"_requestID","type":"uint256"}],"name":"cancelRequest","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getLatestValidatedTx","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_requestID","type":"uint256"}],"name":"getRequest","outputs":[{"name":"spends","type":"bytes32"},{"name":"pays","type":"bytes32"},{"name":"paysValue","type":"uint64"},{"name":"state","type":"uint8"},{"name":"consumer","type":"address"},{"name":"owner","type":"address"},{"name":"numConfs","type":"uint8"},{"name":"notBefore","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spends","type":"bytes"},{"name":"_pays","type":"bytes"},{"name":"_paysValue","type":"uint64"},{"name":"_consumer","type":"address"},{"name":"_numConfs","type":"uint8"},{"name":"_notBefore","type":"uint256"}],"name":"request","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_header","type":"bytes"},{"name":"_proof","type":"bytes"},{"name":"_version","type":"bytes4"},{"name":"_locktime","type":"bytes4"},{"name":"_index","type":"uint256"},{"name":"_reqIndices","type":"uint16"},{"name":"_vin","type":"bytes"},{"name":"_vout","type":"bytes"},{"name":"_requestID","type":"uint256"}],"name":"provideProof","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] From 528fbcf45a65d9f84088bd9ab0f55b42edee2511 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 13:46:18 -0700 Subject: [PATCH 084/148] bug: remove sub expired event --- maintainer/ethereum/contract.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/maintainer/ethereum/contract.py b/maintainer/ethereum/contract.py index 8b37df4d..66224031 100644 --- a/maintainer/ethereum/contract.py +++ b/maintainer/ethereum/contract.py @@ -5,8 +5,8 @@ from maintainer.ethereum import shared from maintainer.relay_abi import ABI as relay_ABI -EXPIRED = events._make_topic0( - abi.find('SubscriptionExpired', relay_ABI)[0]) +from typing import cast + CLOSED = events._make_topic0( abi.find('RequestClosed', relay_ABI)[0]) FILLED = events._make_topic0( @@ -46,7 +46,10 @@ async def is_ancestor( ancestor: bytes, descendant: bytes, limit: int = 240) -> bool: - '''Determine if ancestor precedes descendant''' + ''' + Determine if ancestor precedes descendant + ancestor and descendant MUST be LE + ''' data = calldata.call( "isAncestor", [ancestor, descendant, limit], @@ -87,4 +90,4 @@ async def get_best_block() -> str: 'latest' # block height parameter ] ) - return res[2:] # block-explorer format + return cast(str, res[2:]) # block-explorer format From fa650676a374389d23e492cf16ee01e64a33410e Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 8 Apr 2020 14:25:45 -0700 Subject: [PATCH 085/148] refactor: break out find LCA and add rate limit and error condition --- maintainer/ethereum/contract.py | 4 +++ maintainer/header_forwarder/push.py | 48 ++++++++++++++++++----------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/maintainer/ethereum/contract.py b/maintainer/ethereum/contract.py index 66224031..54b28371 100644 --- a/maintainer/ethereum/contract.py +++ b/maintainer/ethereum/contract.py @@ -31,6 +31,10 @@ async def find_height(digest_le: bytes) -> int: 'latest' # block height parameter ] ) + # if more than 1 ABI slot long, return 0 + if len(res) > 36: + logger.debug(f'findHeight for {digest_le.hex()} is unknown') + return 0 logger.debug(f'findHeight for {digest_le.hex()} is {res}') return int(res, 16) diff --git a/maintainer/header_forwarder/push.py b/maintainer/header_forwarder/push.py index a01ca644..d2690bd1 100644 --- a/maintainer/header_forwarder/push.py +++ b/maintainer/header_forwarder/push.py @@ -122,9 +122,35 @@ async def _add_diff_change(headers: List[RelayHeader]) -> None: asyncio.create_task(shared.sign_and_broadcast(tx)) +async def find_lca( + new_best: RelayHeader, + current_best: RelayHeader +) -> RelayHeader: + # find the latest block in current's history that is an ancestor of new + is_ancestor = False + + for i in range(1, 6): + logger.info(f'Attempt {i} to find LCA in previous 20 blocks') + + ancestor = current_best + for _ in range(20): + is_ancestor = await contract.is_ancestor( + ancestor['hash'], + new_best['hash'] + ) + if is_ancestor: + return ancestor + ancestor = cast( + RelayHeader, + await bcoin_rpc.get_header_by_hash_le(ancestor['prevhash'])) + await asyncio.sleep(15) + raise RuntimeError('Unable to find LCA after 5 attempts') + + # TODO: refactor this to not be shit async def _update_best_digest( - new_best: RelayHeader) -> None: + new_best: RelayHeader +) -> None: '''Send an ethereum transaction that marks a new best known chain tip''' nonce = next(shared.NONCE) will_succeed = False @@ -135,29 +161,15 @@ async def _update_best_digest( RelayHeader, await bcoin_rpc.get_header_by_hash_le(current_best_digest)) - delta = new_best['height'] - current_best['height'] + 1 - - # find the latest block in current's history that is an ancestor of new - is_ancestor = False - ancestor = current_best - while True: - is_ancestor = await contract.is_ancestor( - ancestor['hash'], - new_best['hash']) - if is_ancestor: - break - ancestor = cast( - RelayHeader, - await bcoin_rpc.get_header_by_hash_le(ancestor['prevhash'])) - - ancestor_le = ancestor['hash'] + ancestor = await find_lca(new_best, current_best) + delta = new_best['height'] - ancestor['height'] + 1 tx = shared.make_call_tx( contract=config.get()['CONTRACT'], abi=relay_ABI, method='markNewHeaviest', args=[ - ancestor_le, + ancestor['hash'], current_best["raw"], new_best["raw"], delta], From 22039633baf21035a7e3a693879d93ea70de9ba4 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Thu, 9 Apr 2020 15:09:18 -0700 Subject: [PATCH 086/148] refactor: move maintainer into a subdir --- Pipfile => maintainer/Pipfile | 0 Pipfile.lock => maintainer/Pipfile.lock | 0 maintainer/README.md | 16 ++++++++++++++++ maintainer/{ => maintainer}/__init__.py | 0 maintainer/{ => maintainer}/base.py | 0 maintainer/{ => maintainer}/bitcoin/__init__.py | 0 maintainer/{ => maintainer}/bitcoin/bcoin_rpc.py | 0 maintainer/{ => maintainer}/bitcoin/bsock.py | 0 maintainer/{ => maintainer}/config/.gitignore | 0 maintainer/{ => maintainer}/config/.sample.env | 0 maintainer/{ => maintainer}/config/__init__.py | 0 maintainer/{ => maintainer}/ethereum/__init__.py | 0 maintainer/{ => maintainer}/ethereum/contract.py | 0 maintainer/{ => maintainer}/ethereum/shared.py | 0 .../header_forwarder/__init__.py | 0 .../{ => maintainer}/header_forwarder/h.py | 0 .../{ => maintainer}/header_forwarder/pull.py | 0 .../{ => maintainer}/header_forwarder/push.py | 0 maintainer/{ => maintainer}/relay_abi.py | 0 maintainer/{ => maintainer}/relay_types.py | 0 maintainer/{ => maintainer}/utils.py | 0 setup.py => maintainer/setup.py | 0 22 files changed, 16 insertions(+) rename Pipfile => maintainer/Pipfile (100%) rename Pipfile.lock => maintainer/Pipfile.lock (100%) create mode 100644 maintainer/README.md rename maintainer/{ => maintainer}/__init__.py (100%) rename maintainer/{ => maintainer}/base.py (100%) rename maintainer/{ => maintainer}/bitcoin/__init__.py (100%) rename maintainer/{ => maintainer}/bitcoin/bcoin_rpc.py (100%) rename maintainer/{ => maintainer}/bitcoin/bsock.py (100%) rename maintainer/{ => maintainer}/config/.gitignore (100%) rename maintainer/{ => maintainer}/config/.sample.env (100%) rename maintainer/{ => maintainer}/config/__init__.py (100%) rename maintainer/{ => maintainer}/ethereum/__init__.py (100%) rename maintainer/{ => maintainer}/ethereum/contract.py (100%) rename maintainer/{ => maintainer}/ethereum/shared.py (100%) rename maintainer/{ => maintainer}/header_forwarder/__init__.py (100%) rename maintainer/{ => maintainer}/header_forwarder/h.py (100%) rename maintainer/{ => maintainer}/header_forwarder/pull.py (100%) rename maintainer/{ => maintainer}/header_forwarder/push.py (100%) rename maintainer/{ => maintainer}/relay_abi.py (100%) rename maintainer/{ => maintainer}/relay_types.py (100%) rename maintainer/{ => maintainer}/utils.py (100%) rename setup.py => maintainer/setup.py (100%) diff --git a/Pipfile b/maintainer/Pipfile similarity index 100% rename from Pipfile rename to maintainer/Pipfile diff --git a/Pipfile.lock b/maintainer/Pipfile.lock similarity index 100% rename from Pipfile.lock rename to maintainer/Pipfile.lock diff --git a/maintainer/README.md b/maintainer/README.md new file mode 100644 index 00000000..3fa14883 --- /dev/null +++ b/maintainer/README.md @@ -0,0 +1,16 @@ +Relay Maintainer + +## Setup + +install `pipenv` and `pyenv` + +```sh +$ pipenv install --python=$(pyenv which python3.7) +``` + +## Testing +Current testscript runs linting and typechecking only. + +```sh +$ pipenv run test +``` diff --git a/maintainer/__init__.py b/maintainer/maintainer/__init__.py similarity index 100% rename from maintainer/__init__.py rename to maintainer/maintainer/__init__.py diff --git a/maintainer/base.py b/maintainer/maintainer/base.py similarity index 100% rename from maintainer/base.py rename to maintainer/maintainer/base.py diff --git a/maintainer/bitcoin/__init__.py b/maintainer/maintainer/bitcoin/__init__.py similarity index 100% rename from maintainer/bitcoin/__init__.py rename to maintainer/maintainer/bitcoin/__init__.py diff --git a/maintainer/bitcoin/bcoin_rpc.py b/maintainer/maintainer/bitcoin/bcoin_rpc.py similarity index 100% rename from maintainer/bitcoin/bcoin_rpc.py rename to maintainer/maintainer/bitcoin/bcoin_rpc.py diff --git a/maintainer/bitcoin/bsock.py b/maintainer/maintainer/bitcoin/bsock.py similarity index 100% rename from maintainer/bitcoin/bsock.py rename to maintainer/maintainer/bitcoin/bsock.py diff --git a/maintainer/config/.gitignore b/maintainer/maintainer/config/.gitignore similarity index 100% rename from maintainer/config/.gitignore rename to maintainer/maintainer/config/.gitignore diff --git a/maintainer/config/.sample.env b/maintainer/maintainer/config/.sample.env similarity index 100% rename from maintainer/config/.sample.env rename to maintainer/maintainer/config/.sample.env diff --git a/maintainer/config/__init__.py b/maintainer/maintainer/config/__init__.py similarity index 100% rename from maintainer/config/__init__.py rename to maintainer/maintainer/config/__init__.py diff --git a/maintainer/ethereum/__init__.py b/maintainer/maintainer/ethereum/__init__.py similarity index 100% rename from maintainer/ethereum/__init__.py rename to maintainer/maintainer/ethereum/__init__.py diff --git a/maintainer/ethereum/contract.py b/maintainer/maintainer/ethereum/contract.py similarity index 100% rename from maintainer/ethereum/contract.py rename to maintainer/maintainer/ethereum/contract.py diff --git a/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py similarity index 100% rename from maintainer/ethereum/shared.py rename to maintainer/maintainer/ethereum/shared.py diff --git a/maintainer/header_forwarder/__init__.py b/maintainer/maintainer/header_forwarder/__init__.py similarity index 100% rename from maintainer/header_forwarder/__init__.py rename to maintainer/maintainer/header_forwarder/__init__.py diff --git a/maintainer/header_forwarder/h.py b/maintainer/maintainer/header_forwarder/h.py similarity index 100% rename from maintainer/header_forwarder/h.py rename to maintainer/maintainer/header_forwarder/h.py diff --git a/maintainer/header_forwarder/pull.py b/maintainer/maintainer/header_forwarder/pull.py similarity index 100% rename from maintainer/header_forwarder/pull.py rename to maintainer/maintainer/header_forwarder/pull.py diff --git a/maintainer/header_forwarder/push.py b/maintainer/maintainer/header_forwarder/push.py similarity index 100% rename from maintainer/header_forwarder/push.py rename to maintainer/maintainer/header_forwarder/push.py diff --git a/maintainer/relay_abi.py b/maintainer/maintainer/relay_abi.py similarity index 100% rename from maintainer/relay_abi.py rename to maintainer/maintainer/relay_abi.py diff --git a/maintainer/relay_types.py b/maintainer/maintainer/relay_types.py similarity index 100% rename from maintainer/relay_types.py rename to maintainer/maintainer/relay_types.py diff --git a/maintainer/utils.py b/maintainer/maintainer/utils.py similarity index 100% rename from maintainer/utils.py rename to maintainer/maintainer/utils.py diff --git a/setup.py b/maintainer/setup.py similarity index 100% rename from setup.py rename to maintainer/setup.py From 2150c926528e802cc88de770c1007e990908f0b7 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Thu, 9 Apr 2020 15:09:47 -0700 Subject: [PATCH 087/148] chore: update readmes about maintainer python --- README.md | 8 +------- maintainer/README.md | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 234bcc14..7cc95423 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,5 @@ reducing calldata gas costs. ### Project Notes -The Python relay mainter in `./relay/` is not thoroughly tested, and does not +The Python relay mainter in `./maintainer/` is not thoroughly tested, and does not yet support the cosmos-sdk relay. - -### How do I develop for it? - -install `pipenv` and `pyenv` - -$ pipenv install --python=$(pyenv which python3.7) diff --git a/maintainer/README.md b/maintainer/README.md index 3fa14883..eaf0f824 100644 --- a/maintainer/README.md +++ b/maintainer/README.md @@ -1,4 +1,14 @@ -Relay Maintainer +# Relay Maintainer + +This simple python app maintains the header relay by querying a bcoin node and +pushing headers to an associated geth or infura node. + +Generally it follows the crash-only programming paradigm. Rather than +recovering from errors, we expose them, crash, and emphasize safe resume via a +reboot. + +It is ALPHA-quality software at best. It is not long-term stable. It has poor +handling of bitcoin reorgs, for example. ## Setup @@ -14,3 +24,16 @@ Current testscript runs linting and typechecking only. ```sh $ pipenv run test ``` + +## Running the header forwarder + +Make a config `.env` file in `maintainer/config`. + +```sh +$ cp maintainer/config/.sample.env maintainer/config/.my_env_file.env + +# update the env to point to your BCOIN node, and either geth or infura +$ vim maintainer/config/.env + +$ pipenv run python maintainer/header_forwarder/h.py .my_env_file.env +``` From 6dfe67012656c0f2e23ce7e25e1ac416738e0840 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Fri, 10 Apr 2020 12:35:21 -0700 Subject: [PATCH 088/148] chore: bump mainnet trusted block --- solidity/migrations/networkInfo.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index 188f09cc..88883814 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -6,9 +6,9 @@ const ID_SPACE_SIZE = new BN('2', 10).pow(new BN('32', 10)); const truffleConf = require('../truffle-config'); const bitcoinMain = { - genesis: '0x00000020d208b5e50a8d3bd3a87f7a238e3f196621d0f9ffb5f302000000000000000000ee3af51ad3643a8a109935b45d9ca32b1003cda41df39dd75a17e13ba13aff4211aa585d39301c174a8ead73', - height: 590588, - epochStart: '0x704de08dc5329269011b878835be108a8202a93a0a2a1c000000000000000000', + genesis: '000040202842774747733a4863b6bbb7b4cfb66baa9287d5ce0d13000000000000000000df550e01d02ee37fce8dd2fbf919a47b8b65684bcb48d4da699078916da2f7decbc7905ebc2013178f58d533', + height: 625332, + epochStart: '6e9d58fb0ab8d0181b1c9e54614f80b64004c2e04da310000000000000000000', }; const bitcoinTest = { From 07751a9d6e59ac88e058c81053b821d61f23cabd Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Fri, 10 Apr 2020 12:40:10 -0700 Subject: [PATCH 089/148] bug: missing 0x prefix on hex bytes --- solidity/migrations/networkInfo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index 88883814..58799953 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -6,9 +6,9 @@ const ID_SPACE_SIZE = new BN('2', 10).pow(new BN('32', 10)); const truffleConf = require('../truffle-config'); const bitcoinMain = { - genesis: '000040202842774747733a4863b6bbb7b4cfb66baa9287d5ce0d13000000000000000000df550e01d02ee37fce8dd2fbf919a47b8b65684bcb48d4da699078916da2f7decbc7905ebc2013178f58d533', + genesis: '0x000040202842774747733a4863b6bbb7b4cfb66baa9287d5ce0d13000000000000000000df550e01d02ee37fce8dd2fbf919a47b8b65684bcb48d4da699078916da2f7decbc7905ebc2013178f58d533', height: 625332, - epochStart: '6e9d58fb0ab8d0181b1c9e54614f80b64004c2e04da310000000000000000000', + epochStart: '0x6e9d58fb0ab8d0181b1c9e54614f80b64004c2e04da310000000000000000000', }; const bitcoinTest = { From 3406fbeb3c6d252ce5fb7a0b1b456717db5b82f4 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Fri, 10 Apr 2020 13:27:26 -0700 Subject: [PATCH 090/148] bug: redundant port in truffle-config --- solidity/truffle-config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/solidity/truffle-config.js b/solidity/truffle-config.js index 9e408300..66bad2a7 100644 --- a/solidity/truffle-config.js +++ b/solidity/truffle-config.js @@ -22,7 +22,6 @@ const kovan = { const alfajores = { host: process.env.ALFAJORES_NODE_URL, - port: 8545, // Standard Ethereum port (default: none) network_id: 44786, port: process.env.ALFAJORES_NODE_PORT, from: process.env.ALFAJORES_FROM, From 6c32f9fde89d984ad77afca71e359dd3a9707f3c Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Fri, 10 Apr 2020 16:36:33 -0700 Subject: [PATCH 091/148] feature: update celo to use mnemonic provider instead of unlocked node --- solidity/package-lock.json | 2284 +++++++++++++++++++++++++----------- solidity/package.json | 1 + solidity/truffle-config.js | 23 +- 3 files changed, 1596 insertions(+), 712 deletions(-) diff --git a/solidity/package-lock.json b/solidity/package-lock.json index 3c215b27..3b27a3bd 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -1,6 +1,6 @@ { "name": "@summa-tx/relay-sol", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -30,6 +30,511 @@ "js-tokens": "^4.0.0" } }, + "@celo/contractkit": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@celo/contractkit/-/contractkit-0.3.3.tgz", + "integrity": "sha512-KRIQgqEMDrLAueY7MctsRF+X9iKl1BLSIEo+E6mIaNl62lmSPVXvsAuQ0xuxUYFVJJgn3gPZP7MMlPnPH6aVJQ==", + "requires": { + "@celo/utils": "0.1.8", + "@ledgerhq/hw-app-eth": "^5.11.0", + "@ledgerhq/hw-transport": "^5.11.0", + "@types/debug": "^4.1.5", + "bignumber.js": "^9.0.0", + "cross-fetch": "3.0.4", + "debug": "^4.1.1", + "eth-lib": "^0.2.8", + "ethereumjs-util": "^5.2.0", + "fp-ts": "2.1.1", + "io-ts": "2.0.1", + "web3": "1.2.4", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-eth-abi": "1.2.4", + "web3-eth-contract": "1.2.4", + "web3-utils": "1.2.4" + }, + "dependencies": { + "@types/node": { + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethereumjs-util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "^0.1.3", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "@types/node": { + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==" + }, + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + } + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "web3": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.4.tgz", + "integrity": "sha512-xPXGe+w0x0t88Wj+s/dmAdASr3O9wmA9mpZRtixGZxmBexAF0MjfqYM+MS4tVl5s11hMTN3AZb8cDD4VLfC57A==", + "requires": { + "@types/node": "^12.6.1", + "web3-bzz": "1.2.4", + "web3-core": "1.2.4", + "web3-eth": "1.2.4", + "web3-eth-personal": "1.2.4", + "web3-net": "1.2.4", + "web3-shh": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-bzz": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.4.tgz", + "integrity": "sha512-MqhAo/+0iQSMBtt3/QI1rU83uvF08sYq8r25+OUZ+4VtihnYsmkkca+rdU0QbRyrXY2/yGIpI46PFdh0khD53A==", + "requires": { + "@types/node": "^10.12.18", + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + }, + "dependencies": { + "@types/node": { + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==" + } + } + }, + "web3-core": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.4.tgz", + "integrity": "sha512-CHc27sMuET2cs1IKrkz7xzmTdMfZpYswe7f0HcuyneTwS1yTlTnHyqjAaTy0ZygAb/x4iaVox+Gvr4oSAqSI+A==", + "requires": { + "@types/bignumber.js": "^5.0.0", + "@types/bn.js": "^4.11.4", + "@types/node": "^12.6.1", + "web3-core-helpers": "1.2.4", + "web3-core-method": "1.2.4", + "web3-core-requestmanager": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-core-helpers": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.4.tgz", + "integrity": "sha512-U7wbsK8IbZvF3B7S+QMSNP0tni/6VipnJkB0tZVEpHEIV2WWeBHYmZDnULWcsS/x/jn9yKhJlXIxWGsEAMkjiw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-core-method": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.4.tgz", + "integrity": "sha512-8p9kpL7di2qOVPWgcM08kb+yKom0rxRCMv6m/K+H+yLSxev9TgMbCgMSbPWAHlyiF3SJHw7APFKahK5Z+8XT5A==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.4", + "web3-core-promievent": "1.2.4", + "web3-core-subscriptions": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-core-promievent": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.4.tgz", + "integrity": "sha512-gEUlm27DewUsfUgC3T8AxkKi8Ecx+e+ZCaunB7X4Qk3i9F4C+5PSMGguolrShZ7Zb6717k79Y86f3A00O0VAZw==", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + } + }, + "web3-core-requestmanager": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.4.tgz", + "integrity": "sha512-eZJDjyNTDtmSmzd3S488nR/SMJtNnn/GuwxnMh3AzYCqG3ZMfOylqTad2eYJPvc2PM5/Gj1wAMQcRpwOjjLuPg==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.4", + "web3-providers-http": "1.2.4", + "web3-providers-ipc": "1.2.4", + "web3-providers-ws": "1.2.4" + } + }, + "web3-core-subscriptions": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.4.tgz", + "integrity": "sha512-3D607J2M8ymY9V+/WZq4MLlBulwCkwEjjC2U+cXqgVO1rCyVqbxZNCmHyNYHjDDCxSEbks9Ju5xqJxDSxnyXEw==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.4" + } + }, + "web3-eth": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.4.tgz", + "integrity": "sha512-+j+kbfmZsbc3+KJpvHM16j1xRFHe2jBAniMo1BHKc3lho6A8Sn9Buyut6odubguX2AxoRArCdIDCkT9hjUERpA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-core-method": "1.2.4", + "web3-core-subscriptions": "1.2.4", + "web3-eth-abi": "1.2.4", + "web3-eth-accounts": "1.2.4", + "web3-eth-contract": "1.2.4", + "web3-eth-ens": "1.2.4", + "web3-eth-iban": "1.2.4", + "web3-eth-personal": "1.2.4", + "web3-net": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-eth-abi": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.4.tgz", + "integrity": "sha512-8eLIY4xZKoU3DSVu1pORluAw9Ru0/v4CGdw5so31nn+7fR8zgHMgwbFe0aOqWQ5VU42PzMMXeIJwt4AEi2buFg==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.4" + } + }, + "web3-eth-accounts": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.4.tgz", + "integrity": "sha512-04LzT/UtWmRFmi4hHRewP5Zz43fWhuHiK5XimP86sUQodk/ByOkXQ3RoXyGXFMNoRxdcAeRNxSfA2DpIBc9xUw==", + "requires": { + "@web3-js/scrypt-shim": "^0.1.0", + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "ethereumjs-common": "^1.3.2", + "ethereumjs-tx": "^2.1.1", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-core-method": "1.2.4", + "web3-utils": "1.2.4" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.4.tgz", + "integrity": "sha512-b/9zC0qjVetEYnzRA1oZ8gF1OSSUkwSYi5LGr4GeckLkzXP7osEnp9lkO/AQcE4GpG+l+STnKPnASXJGZPgBRQ==", + "requires": { + "@types/bn.js": "^4.11.4", + "underscore": "1.9.1", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-core-method": "1.2.4", + "web3-core-promievent": "1.2.4", + "web3-core-subscriptions": "1.2.4", + "web3-eth-abi": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-eth-ens": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.4.tgz", + "integrity": "sha512-g8+JxnZlhdsCzCS38Zm6R/ngXhXzvc3h7bXlxgKU4coTzLLoMpgOAEz71GxyIJinWTFbLXk/WjNY0dazi9NwVw==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-core-promievent": "1.2.4", + "web3-eth-abi": "1.2.4", + "web3-eth-contract": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-eth-iban": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.4.tgz", + "integrity": "sha512-D9HIyctru/FLRpXakRwmwdjb5bWU2O6UE/3AXvRm6DCOf2e+7Ve11qQrPtaubHfpdW3KWjDKvlxV9iaFv/oTMQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.4" + } + }, + "web3-eth-personal": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.4.tgz", + "integrity": "sha512-5Russ7ZECwHaZXcN3DLuLS7390Vzgrzepl4D87SD6Sn1DHsCZtvfdPIYwoTmKNp69LG3mORl7U23Ga5YxqkICw==", + "requires": { + "@types/node": "^12.6.1", + "web3-core": "1.2.4", + "web3-core-helpers": "1.2.4", + "web3-core-method": "1.2.4", + "web3-net": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-net": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.4.tgz", + "integrity": "sha512-wKOsqhyXWPSYTGbp7ofVvni17yfRptpqoUdp3SC8RAhDmGkX6irsiT9pON79m6b3HUHfLoBilFQyt/fTUZOf7A==", + "requires": { + "web3-core": "1.2.4", + "web3-core-method": "1.2.4", + "web3-utils": "1.2.4" + } + }, + "web3-providers-http": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.4.tgz", + "integrity": "sha512-dzVCkRrR/cqlIrcrWNiPt9gyt0AZTE0J+MfAu9rR6CyIgtnm1wFUVVGaxYRxuTGQRO4Dlo49gtoGwaGcyxqiTw==", + "requires": { + "web3-core-helpers": "1.2.4", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.4.tgz", + "integrity": "sha512-8J3Dguffin51gckTaNrO3oMBo7g+j0UNk6hXmdmQMMNEtrYqw4ctT6t06YOf9GgtOMjSAc1YEh3LPrvgIsR7og==", + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.4" + } + }, + "web3-providers-ws": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.4.tgz", + "integrity": "sha512-F/vQpDzeK+++oeeNROl1IVTufFCwCR2hpWe5yRXN0ApLwHqXrMI7UwQNdJ9iyibcWjJf/ECbauEEQ8CHgE+MYQ==", + "requires": { + "@web3-js/websocket": "^1.0.29", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.4" + } + }, + "web3-shh": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.4.tgz", + "integrity": "sha512-z+9SCw0dE+69Z/Hv8809XDbLj7lTfEv9Sgu8eKEIdGntZf4v7ewj5rzN5bZZSz8aCvfK7Y6ovz1PBAu4QzS4IQ==", + "requires": { + "web3-core": "1.2.4", + "web3-core-method": "1.2.4", + "web3-core-subscriptions": "1.2.4", + "web3-net": "1.2.4" + } + }, + "web3-utils": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.4.tgz", + "integrity": "sha512-+S86Ip+jqfIPQWvw2N/xBQq5JNqCO0dyvukGdJm8fEWHZbckT4WxSpHbx+9KLEWY4H4x9pUwnoRkK87pYyHfgQ==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethereum-bloom-filters": "^1.0.6", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "underscore": "1.9.1", + "utf8": "3.0.0" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + } + } + } + } + }, + "@celo/utils": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@celo/utils/-/utils-0.1.8.tgz", + "integrity": "sha512-Zgr/N7wHTdMsc+C519JkZCqe6rfrNW8LSnscTISi5NfMTPbvDs/TvWrcuKi36JLVU5SYAPx55JGrSVOhQ1mmeg==", + "requires": { + "@umpirsky/country-list": "git://github.com/umpirsky/country-list.git#05fda51", + "bigi": "^1.1.0", + "bignumber.js": "^7.2.0", + "bls12377js": "git+https://github.com/celo-org/bls12377js.git#400bcaeec9e7620b040bfad833268f5289699cac", + "bn.js": "4.11.8", + "buffer-reverse": "^1.0.1", + "country-data": "^0.0.31", + "crypto-js": "^3.1.9-1", + "elliptic": "^6.4.1", + "ethereumjs-util": "^5.2.0", + "futoin-hkdf": "^1.0.3", + "google-libphonenumber": "^3.2.4", + "keccak256": "^1.0.0", + "lodash": "^4.17.14", + "numeral": "^2.0.6", + "web3-utils": "1.2.4" + }, + "dependencies": { + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "ethereumjs-util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.0.tgz", + "integrity": "sha512-CJAKdI0wgMbQFLlLRtZKGcy/L6pzVRgelIZqRqNbuVFM3K9VEnyfbcvz0ncWMRNCe4kaHWjwRYQcYMucmwsnWA==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "^0.1.3", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, + "web3-utils": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.4.tgz", + "integrity": "sha512-+S86Ip+jqfIPQWvw2N/xBQq5JNqCO0dyvukGdJm8fEWHZbckT4WxSpHbx+9KLEWY4H4x9pUwnoRkK87pYyHfgQ==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethereum-bloom-filters": "^1.0.6", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "underscore": "1.9.1", + "utf8": "3.0.0" + } + } + } + }, "@ethersproject/abi": { "version": "5.0.0-beta.142", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.0-beta.142.tgz", @@ -165,6 +670,56 @@ "@ethersproject/logger": ">=5.0.0-beta.129" } }, + "@ledgerhq/devices": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-5.13.0.tgz", + "integrity": "sha512-jx3qX4dOkJpOL/TlnuzAwVcOm/IDCFvhXvfIAxu7F9dhafHDqYP0+8uHKBeJtkLyA4wd63SbHXVo16xmsAHp4Q==", + "requires": { + "@ledgerhq/errors": "^5.13.0", + "@ledgerhq/logs": "^5.13.0", + "rxjs": "^6.5.5" + }, + "dependencies": { + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "requires": { + "tslib": "^1.9.0" + } + } + } + }, + "@ledgerhq/errors": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-5.13.0.tgz", + "integrity": "sha512-I+13snTaDZQbhnbxe3Hwud3bkmDqDSe/s8z0dzkhbchFdXvmtp77IbQrbJZ2m4L5W2bOBHAhv6Dz2SZv5Ll/VA==" + }, + "@ledgerhq/hw-app-eth": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-app-eth/-/hw-app-eth-5.13.0.tgz", + "integrity": "sha512-1Ru/ke/eK2mNDAF1Sk0QHogba5ta/nqX0E0FJyhW4D6mOxo3q8baJzK4sIb8UAEV5K2hF2dttG9awnUfNmiT7Q==", + "requires": { + "@ledgerhq/errors": "^5.13.0", + "@ledgerhq/hw-transport": "^5.13.0", + "bignumber.js": "^9.0.0" + } + }, + "@ledgerhq/hw-transport": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-5.13.0.tgz", + "integrity": "sha512-IUwmW3YTULWZyuw5JNgGCTmPZs80XJRq5vLi6nH53Ouvgz1yVy+ktNALOcaOxyR2WRA1flwRTRiss/OtJrCDjQ==", + "requires": { + "@ledgerhq/devices": "^5.13.0", + "@ledgerhq/errors": "^5.13.0", + "events": "^3.1.0" + } + }, + "@ledgerhq/logs": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-5.13.0.tgz", + "integrity": "sha512-yMvzQiMjWDMRma3HPxQQibhvEqMaEdXXkNBk1+eaW+N47Y3neYSSyCJlyihzzMBeeoin4ChlP/5uKQaABwBeTg==" + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -194,8 +749,50 @@ "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", - "dev": true + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@stablelib/binary": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-0.7.2.tgz", + "integrity": "sha1-GzOSFwyKh0HIuPhD6ilN5xrrLPc=", + "requires": { + "@stablelib/int": "^0.5.0" + } + }, + "@stablelib/blake2s": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@stablelib/blake2s/-/blake2s-0.10.4.tgz", + "integrity": "sha512-IasdklC7YfXXLmVbnsxqmd66+Ki+Ysbp0BtcrNxAtrGx/HRGjkUZbSTbEa7HxFhBWIstJRcE5ExgY+RCqAiULQ==", + "requires": { + "@stablelib/binary": "^0.7.2", + "@stablelib/hash": "^0.5.0", + "@stablelib/wipe": "^0.5.0" + } + }, + "@stablelib/blake2xs": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@stablelib/blake2xs/-/blake2xs-0.10.4.tgz", + "integrity": "sha512-1N0S4cruso/StV9TmoujPGj3RU0Cy42wlZneBWLWby7m2ssnY57l/CsYQSm03TshOoYss4hqc5kwSy5pmWAdUA==", + "requires": { + "@stablelib/blake2s": "^0.10.4", + "@stablelib/hash": "^0.5.0", + "@stablelib/wipe": "^0.5.0" + } + }, + "@stablelib/hash": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-0.5.0.tgz", + "integrity": "sha1-if6QQKPUODsZIcfYpglIvDCEYGg=" + }, + "@stablelib/int": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-0.5.0.tgz", + "integrity": "sha1-zKkiWVHVXS3khlZ1V4R4hjNmDCs=" + }, + "@stablelib/wipe": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-0.5.0.tgz", + "integrity": "sha1-poLV+USOlQ4JnlN+b3L8lgJ10VE=" }, "@summa-tx/bitcoin-spv-sol": { "version": "2.2.0", @@ -206,7 +803,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, "requires": { "defer-to-connect": "^1.0.1" } @@ -576,11 +1172,18 @@ "web3": "1.2.1" } }, + "@types/bignumber.js": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/bignumber.js/-/bignumber.js-5.0.0.tgz", + "integrity": "sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==", + "requires": { + "bignumber.js": "*" + } + }, "@types/bn.js": { "version": "4.11.5", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.5.tgz", "integrity": "sha512-AEAZcIZga0JgVMHNtl1CprA/hXX7/wPt79AgR4XqaDt7jyj3QWYw6LPoOiznPtugDmlubUnAahMs2PFxGcQrng==", - "dev": true, "requires": { "@types/node": "*" } @@ -600,6 +1203,11 @@ "@types/node": "*" } }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -635,8 +1243,7 @@ "@types/node": { "version": "10.14.18", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.18.tgz", - "integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==", - "dev": true + "integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==" }, "@types/qs": { "version": "6.9.1", @@ -644,11 +1251,14 @@ "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==", "dev": true }, + "@umpirsky/country-list": { + "version": "git://github.com/umpirsky/country-list.git#05fda51cd97b3294e8175ffed06104c44b3c71d7", + "from": "git://github.com/umpirsky/country-list.git#05fda51" + }, "@web3-js/scrypt-shim": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@web3-js/scrypt-shim/-/scrypt-shim-0.1.0.tgz", "integrity": "sha512-ZtZeWCc/s0nMcdx/+rZwY1EcuRdemOK9ag21ty9UsHkFxsNb/AaoucUz0iPuyGe0Ku+PFuRmWZG7Z7462p9xPw==", - "dev": true, "requires": { "scryptsy": "^2.1.0", "semver": "^6.3.0" @@ -658,7 +1268,6 @@ "version": "1.0.30", "resolved": "https://registry.npmjs.org/@web3-js/websocket/-/websocket-1.0.30.tgz", "integrity": "sha512-fDwrD47MiDrzcJdSeTLF75aCcxVVt8B1N74rA+vh2XCAvFy4tEWJjtnUtj2QG7/zlQ6g9cQ88bZFBxwd9/FmtA==", - "dev": true, "requires": { "debug": "^2.2.0", "es5-ext": "^0.10.50", @@ -671,7 +1280,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -679,14 +1287,12 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" } } }, @@ -700,7 +1306,6 @@ "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, "requires": { "mime-types": "~2.1.24", "negotiator": "0.6.2" @@ -727,14 +1332,12 @@ "aes-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=", - "dev": true + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -749,6 +1352,11 @@ "dev": true, "optional": true }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==" + }, "ansi-escapes": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", @@ -769,14 +1377,12 @@ "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -784,8 +1390,7 @@ "any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", - "dev": true + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" }, "anymatch": { "version": "1.3.2", @@ -797,11 +1402,15 @@ "normalize-path": "^2.0.0" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -830,8 +1439,7 @@ "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, "array-includes": { "version": "3.1.1", @@ -1038,7 +1646,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -1047,7 +1654,6 @@ "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -1057,16 +1663,19 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "assign-symbols": { "version": "1.0.0", @@ -1095,14 +1704,12 @@ "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { "version": "2.1.2", @@ -1113,14 +1720,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axios": { "version": "0.19.2", @@ -1134,8 +1739,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -1179,85 +1783,300 @@ "kind-of": "^6.0.0" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==" + }, + "bigi": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz", + "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU=" + }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + } + }, + "bls12377js": { + "version": "git+https://github.com/celo-org/bls12377js.git#400bcaeec9e7620b040bfad833268f5289699cac", + "from": "git+https://github.com/celo-org/bls12377js.git#400bcaeec9e7620b040bfad833268f5289699cac", + "requires": { + "@stablelib/blake2xs": "0.10.4", + "@types/node": "^12.11.7", + "big-integer": "^1.6.44", + "chai": "^4.2.0", + "mocha": "^6.2.2", + "ts-node": "^8.4.1", + "typescript": "^3.6.4" + }, + "dependencies": { + "@types/node": { + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==" + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mkdirp": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", + "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mocha": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", + "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "find-up": "3.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.13.1", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.4", + "ms": "2.1.1", + "node-environment-flags": "1.0.5", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "1.6.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true } } }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dev": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, - "bip66": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", - "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "dev": true, - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "bluebird": { "version": "3.5.5", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", - "dev": true + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" }, "bn.js": { "version": "5.1.1", @@ -1268,7 +2087,6 @@ "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", - "dev": true, "requires": { "bytes": "3.1.0", "content-type": "~1.0.4", @@ -1286,7 +2104,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -1294,14 +2111,12 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" } } }, @@ -1309,7 +2124,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1329,20 +2143,17 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, "browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, "requires": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -1356,7 +2167,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, "requires": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -1367,7 +2177,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -1379,7 +2188,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, "requires": { "bn.js": "^4.1.0", "randombytes": "^2.0.1" @@ -1388,8 +2196,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -1397,7 +2204,6 @@ "version": "0.0.4", "resolved": "https://registry.npmjs.org/browserify-sha3/-/browserify-sha3-0.0.4.tgz", "integrity": "sha1-CGxHuMgjFsnUcCLCYYWVRXbdjiY=", - "dev": true, "requires": { "js-sha3": "^0.6.1", "safe-buffer": "^5.1.1" @@ -1406,8 +2212,7 @@ "js-sha3": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.6.1.tgz", - "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=", - "dev": true + "integrity": "sha1-W4n3enR3Z5h39YxKB1JAk0sflcA=" } } }, @@ -1415,7 +2220,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, "requires": { "bn.js": "^4.1.1", "browserify-rsa": "^4.0.0", @@ -1429,8 +2233,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -1438,7 +2241,6 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz", "integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==", - "dev": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -1448,7 +2250,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -1457,44 +2258,42 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", - "dev": true + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "dev": true + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-reverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", + "integrity": "sha1-SSg8jvpvkBvAH6MwTQYCeXGuL2A=" }, "buffer-to-arraybuffer": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", - "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=", - "dev": true + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, "cache-base": { "version": "1.0.1", @@ -1525,7 +2324,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, "requires": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -1540,7 +2338,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -1548,14 +2345,12 @@ "http-cache-semantics": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", - "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==", - "dev": true + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" }, "lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" } } }, @@ -1568,20 +2363,30 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1600,6 +2405,11 @@ "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", "dev": true }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -1646,14 +2456,12 @@ "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -1793,7 +2601,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, "requires": { "mimic-response": "^1.0.0" } @@ -1824,7 +2631,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -1832,8 +2638,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.3.3", @@ -1845,7 +2650,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -1865,8 +2669,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -1896,7 +2699,6 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "dev": true, "requires": { "safe-buffer": "5.1.2" }, @@ -1904,34 +2706,29 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", - "dev": true + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", - "dev": true + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", - "dev": true + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" }, "cookiejar": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", - "dev": true + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" }, "copy-descriptor": { "version": "0.1.1", @@ -1942,24 +2739,30 @@ "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "requires": { "object-assign": "^4", "vary": "^1" } }, + "country-data": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/country-data/-/country-data-0.0.31.tgz", + "integrity": "sha1-gJZrjh0Uf6bWpYnTKTP4eTd0lW0=", + "requires": { + "currency-symbol-map": "~2", + "underscore": ">1.4.4" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, "requires": { "bn.js": "^4.1.0", "elliptic": "^6.0.0" @@ -1968,8 +2771,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -1977,7 +2779,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, "requires": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -1990,7 +2791,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, "requires": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -2000,6 +2800,15 @@ "sha.js": "^2.4.8" } }, + "cross-fetch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz", + "integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==", + "requires": { + "node-fetch": "2.6.0", + "whatwg-fetch": "3.0.0" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2031,7 +2840,6 @@ "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, "requires": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -2046,11 +2854,20 @@ "randomfill": "^1.0.3" } }, + "crypto-js": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz", + "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==" + }, + "currency-symbol-map": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/currency-symbol-map/-/currency-symbol-map-2.2.0.tgz", + "integrity": "sha1-KzwYcv8aws5ZXYJz5Y4f/wJyrqI=" + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, "requires": { "es5-ext": "^0.10.50", "type": "^1.0.1" @@ -2060,7 +2877,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -2075,7 +2891,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -2083,20 +2898,17 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", - "dev": true, "requires": { "decompress-tar": "^4.0.0", "decompress-tarbz2": "^4.0.0", @@ -2112,7 +2924,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", - "dev": true, "requires": { "mimic-response": "^1.0.0" } @@ -2121,7 +2932,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", - "dev": true, "requires": { "file-type": "^5.2.0", "is-stream": "^1.1.0", @@ -2132,7 +2942,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", - "dev": true, "requires": { "decompress-tar": "^4.1.0", "file-type": "^6.1.0", @@ -2144,8 +2953,7 @@ "file-type": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", - "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", - "dev": true + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" } } }, @@ -2153,7 +2961,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", - "dev": true, "requires": { "decompress-tar": "^4.1.1", "file-type": "^5.2.0", @@ -2164,7 +2971,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", - "dev": true, "requires": { "file-type": "^3.8.0", "get-stream": "^2.2.0", @@ -2175,14 +2981,12 @@ "file-type": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", - "dev": true + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "get-stream": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", - "dev": true, "requires": { "object-assign": "^4.0.1", "pinkie-promise": "^2.0.0" @@ -2190,6 +2994,14 @@ } } }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2199,14 +3011,12 @@ "defer-to-connect": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", - "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==", - "dev": true + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==" }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -2273,20 +3083,17 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", - "dev": true + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, "requires": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -2295,8 +3102,7 @@ "destroy": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", - "dev": true + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, "detect-port": { "version": "1.3.0", @@ -2328,14 +3134,12 @@ "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "dev": true, "requires": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -2345,8 +3149,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -2379,8 +3182,7 @@ "dom-walk": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.1.tgz", - "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=", - "dev": true + "integrity": "sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=" }, "dotenv": { "version": "8.2.0", @@ -2391,7 +3193,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", - "dev": true, "requires": { "browserify-aes": "^1.0.6", "create-hash": "^1.1.2", @@ -2401,14 +3202,12 @@ "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -2417,14 +3216,12 @@ "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", - "dev": true + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "elliptic": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2435,28 +3232,24 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", - "dev": true + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, "requires": { "once": "^1.4.0" } @@ -2480,7 +3273,6 @@ "version": "1.14.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.14.2.tgz", "integrity": "sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg==", - "dev": true, "requires": { "es-to-primitive": "^1.2.0", "function-bind": "^1.1.1", @@ -2498,7 +3290,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -2509,7 +3300,6 @@ "version": "0.10.51", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.51.tgz", "integrity": "sha512-oRpWzM2WcLHVKpnrcyB7OW8j/s67Ba04JCm0WnNv3RiABSvs7mrQlutB8DBv793gKcp0XENR8Il8WxGTlZ73gQ==", - "dev": true, "requires": { "es6-iterator": "~2.0.3", "es6-symbol": "~3.1.1", @@ -2520,7 +3310,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, "requires": { "d": "1", "es5-ext": "^0.10.35", @@ -2531,7 +3320,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.2.tgz", "integrity": "sha512-/ZypxQsArlv+KHpGvng52/Iz8by3EQPxhmbuz8yFG89N/caTFBSbcXONDw0aMjy827gQg26XAjP4uXFvnfINmQ==", - "dev": true, "requires": { "d": "^1.0.1", "es5-ext": "^0.10.51" @@ -2552,14 +3340,12 @@ "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.8.1", @@ -2818,8 +3604,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { "version": "1.2.0", @@ -2862,14 +3647,12 @@ "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", - "dev": true + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, "eth-ens-namehash": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", - "dev": true, "requires": { "idna-uts46-hx": "^2.3.1", "js-sha3": "^0.5.7" @@ -2948,7 +3731,6 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", - "dev": true, "requires": { "bn.js": "^4.11.6", "elliptic": "^6.4.0", @@ -2958,14 +3740,12 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "elliptic": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2982,7 +3762,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.6.tgz", "integrity": "sha512-dE9CGNzgOOsdh7msZirvv8qjHtnHpvBlKe2647kM8v+yeF71IRso55jpojemvHV+jMjr48irPWxMRaHuOWzAFA==", - "dev": true, "requires": { "js-sha3": "^0.8.0" }, @@ -2990,22 +3769,19 @@ "js-sha3": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" } } }, "ethereumjs-common": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.0.tgz", - "integrity": "sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ==", - "dev": true + "integrity": "sha512-SZOjgK1356hIY7MRj3/ma5qtfr/4B5BL+G4rP/XSMYr2z1H5el4RX5GReYCKmQmYI/nSBmRnwrZ17IfHuG0viQ==" }, "ethereumjs-tx": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", - "dev": true, "requires": { "ethereumjs-common": "^1.5.0", "ethereumjs-util": "^6.0.0" @@ -3015,7 +3791,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.0.tgz", "integrity": "sha512-vb0XN9J2QGdZGIEKG2vXM+kUdEivUfU6Wmi5y0cg+LRhDYKnXIZ/Lz7XjFbHRR9VIKq2lVGLzGBkA++y2nOdOQ==", - "dev": true, "requires": { "@types/bn.js": "^4.11.3", "bn.js": "^4.11.0", @@ -3029,8 +3804,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -3084,7 +3858,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", - "dev": true, "requires": { "bn.js": "4.11.6", "number-to-bn": "1.7.0" @@ -3093,8 +3866,7 @@ "bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", - "dev": true + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" } } }, @@ -3102,7 +3874,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", - "dev": true, "requires": { "is-hex-prefixed": "1.0.0", "strip-hex-prefix": "1.0.0" @@ -3121,14 +3892,17 @@ "eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, "requires": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -3206,7 +3980,6 @@ "version": "4.17.1", "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", - "dev": true, "requires": { "accepts": "~1.3.7", "array-flatten": "1.1.1", @@ -3244,7 +4017,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -3252,28 +4024,24 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "qs": { "version": "6.7.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", - "dev": true + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -3327,14 +4095,12 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-glob": { "version": "3.2.2", @@ -3398,8 +4164,7 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "fast-levenshtein": { "version": "2.0.6", @@ -3420,7 +4185,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", - "dev": true, "requires": { "pend": "~1.2.0" } @@ -3446,14 +4210,12 @@ "file-type": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", - "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", - "dev": true + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "filename-regex": { "version": "2.0.1", @@ -3478,7 +4240,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, "requires": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -3493,7 +4254,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -3501,8 +4261,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -3515,6 +4274,21 @@ "locate-path": "^2.0.0" } }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + } + } + }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -3562,7 +4336,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, "requires": { "is-callable": "^1.1.3" } @@ -3585,14 +4358,12 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3602,8 +4373,12 @@ "forwarded": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", - "dev": true + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fp-ts": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.1.1.tgz", + "integrity": "sha512-YcWhMdDCFCja0MmaDroTgNu+NWWrrnUEn92nvDgrtVy9Z71YFnhNVIghoHPt8gs82ijoMzFGeWKvArbyICiJgw==" }, "fragment-cache": { "version": "0.2.1", @@ -3617,20 +4392,17 @@ "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", - "dev": true + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -3641,7 +4413,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, "requires": { "minipass": "^2.6.0" } @@ -3655,8 +4426,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.12", @@ -4212,8 +4982,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -4221,6 +4990,11 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "futoin-hkdf": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.3.2.tgz", + "integrity": "sha512-3EVi3ETTyJg5PSXlxLCaUVVn0pSbDf62L3Gwxne7Uq+d8adOSNWQAad4gg7WToHkcgnCJb3Wlb1P8r4Evj4GPw==" + }, "ganache-cli": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/ganache-cli/-/ganache-cli-6.9.0.tgz", @@ -4900,6 +5674,11 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, "get-port": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", @@ -4910,7 +5689,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, "requires": { "pump": "^3.0.0" } @@ -4925,7 +5703,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -5003,7 +5780,6 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", - "dev": true, "requires": { "min-document": "^2.19.0", "process": "~0.5.1" @@ -5078,11 +5854,15 @@ } } }, + "google-libphonenumber": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.8.tgz", + "integrity": "sha512-iWs1KcxOozmKQbCeGjvU0M7urrkNjBYOSBtb819RjkUNJHJLfn7DADKkKwdJTOMPLcLOE11/4h/FyFwJsTiwLg==" + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, "requires": { "@sindresorhus/is": "^0.14.0", "@szmarczak/http-timer": "^1.1.2", @@ -5100,20 +5880,17 @@ "graceful-fs": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", - "dev": true + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" }, "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" }, "handlebars": { "version": "4.7.5", @@ -5255,14 +6032,12 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -5272,7 +6047,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -5280,26 +6054,22 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbol-support-x": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", - "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", - "dev": true + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", - "dev": true + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", - "dev": true, "requires": { "has-symbol-support-x": "^1.4.1" } @@ -5368,7 +6138,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -5378,7 +6147,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.0" @@ -5394,7 +6162,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -5423,7 +6190,6 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "dev": true, "requires": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -5435,16 +6201,14 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" } } }, "http-https": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", - "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=", - "dev": true + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" }, "http-response-object": { "version": "3.0.2", @@ -5459,7 +6223,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -5470,7 +6233,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -5479,7 +6241,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", - "dev": true, "requires": { "punycode": "2.1.0" }, @@ -5487,16 +6248,14 @@ "punycode": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", - "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=", - "dev": true + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" } } }, "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ignore": { "version": "4.0.6", @@ -5524,7 +6283,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -5533,8 +6291,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.5", @@ -5665,11 +6422,15 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "io-ts": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.0.1.tgz", + "integrity": "sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ==" + }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", - "dev": true + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -5704,8 +6465,7 @@ "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-data-descriptor": { "version": "0.1.4", @@ -5719,8 +6479,7 @@ "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-descriptor": { "version": "0.1.6", @@ -5771,14 +6530,12 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "is-function": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", - "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", - "dev": true + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=" }, "is-glob": { "version": "4.0.1", @@ -5792,14 +6549,12 @@ "is-hex-prefixed": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", - "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=", - "dev": true + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" }, "is-natural-number": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", - "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", - "dev": true + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" }, "is-number": { "version": "2.1.0", @@ -5813,14 +6568,12 @@ "is-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", - "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", - "dev": true + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" }, "is-plain-object": { "version": "2.0.4", @@ -5861,7 +6614,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, "requires": { "has": "^1.0.1" } @@ -5869,14 +6621,12 @@ "is-retry-allowed": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-string": { "version": "1.0.5", @@ -5888,7 +6638,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "dev": true, "requires": { "has-symbols": "^1.0.0" } @@ -5896,8 +6645,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-windows": { "version": "1.0.2", @@ -5908,14 +6656,12 @@ "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "2.1.0", @@ -5929,14 +6675,12 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "isurl": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", - "dev": true, "requires": { "has-to-string-tag-x": "^1.2.0", "is-object": "^1.0.1" @@ -5945,8 +6689,7 @@ "js-sha3": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", - "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=", - "dev": true + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" }, "js-string-escape": { "version": "1.0.1", @@ -5964,7 +6707,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -5973,26 +6715,22 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -6003,14 +6741,12 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -6025,7 +6761,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -6037,7 +6772,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/keccak/-/keccak-2.1.0.tgz", "integrity": "sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q==", - "dev": true, "requires": { "bindings": "^1.5.0", "inherits": "^2.0.4", @@ -6048,8 +6782,34 @@ "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + } + } + }, + "keccak256": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.0.tgz", + "integrity": "sha512-8qv2vJdQk+Aa2tFXo8zYodm+6DgXqUOqvNJhj1p1V2pxQJT1oNKxNF+zWfhtKXNLZdLvyxjB/dvd9GwcvTHSQQ==", + "requires": { + "bn.js": "^4.11.8", + "keccak": "^1.4.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } } } }, @@ -6057,7 +6817,6 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", "integrity": "sha512-BjLkNDcfaZ6l8HBG9tH0tpmDv3sS2mA7FNQxFHpCdzP3Gb2MVruXBSuoM66SnVxKJpAr5dKGdkHD+bDokt8fTg==", - "dev": true, "requires": { "browserify-sha3": "^0.0.4", "sha3": "^1.2.2" @@ -6067,7 +6826,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, "requires": { "json-buffer": "3.0.0" } @@ -6125,8 +6883,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.toarray": { "version": "4.4.0", @@ -6134,11 +6891,18 @@ "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", "dev": true }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "requires": { + "chalk": "^2.0.1" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, "lru-queue": { "version": "0.1.0", @@ -6153,7 +6917,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, "requires": { "pify": "^3.0.0" }, @@ -6161,11 +6924,15 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -6197,7 +6964,6 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -6207,8 +6973,7 @@ "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", - "dev": true + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "mem": { "version": "1.1.0", @@ -6238,8 +7003,7 @@ "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", - "dev": true + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, "merge2": { "version": "1.3.0", @@ -6250,8 +7014,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "dev": true + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, "micromatch": { "version": "2.3.11", @@ -6295,7 +7058,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, "requires": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -6304,28 +7066,24 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, "requires": { "mime-db": "1.40.0" } @@ -6339,14 +7097,12 @@ "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", - "dev": true, "requires": { "dom-walk": "^0.1.0" } @@ -6354,20 +7110,17 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6375,14 +7128,12 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "minipass": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6392,7 +7143,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, "requires": { "minipass": "^2.9.0" } @@ -6421,14 +7171,12 @@ "mkdirp": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz", - "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==", - "dev": true + "integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==" }, "mkdirp-promise": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", - "dev": true, "requires": { "mkdirp": "*" } @@ -6510,14 +7258,12 @@ "mock-fs": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.10.1.tgz", - "integrity": "sha512-w22rOL5ZYu6HbUehB5deurghGM0hS/xBVyHMGKOuQctkk93J9z9VEOhDsiWrXOprVNQpP9uzGKdl8v9mFspKuw==", - "dev": true + "integrity": "sha512-w22rOL5ZYu6HbUehB5deurghGM0hS/xBVyHMGKOuQctkk93J9z9VEOhDsiWrXOprVNQpP9uzGKdl8v9mFspKuw==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mute-stream": { "version": "0.0.8", @@ -6528,14 +7274,12 @@ "nan": { "version": "2.13.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==" }, "nano-json-stream-parser": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", - "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=", - "dev": true + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" }, "nanomatch": { "version": "1.2.13", @@ -6585,8 +7329,7 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { "version": "2.6.1", @@ -6597,8 +7340,7 @@ "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", - "dev": true + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" }, "nice-try": { "version": "1.0.5", @@ -6615,6 +7357,27 @@ "lodash.toarray": "^4.4.0" } }, + "node-environment-flags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", + "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -6656,8 +7419,7 @@ "normalize-url": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.3.0.tgz", - "integrity": "sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==", - "dev": true + "integrity": "sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==" }, "npm-run-path": { "version": "2.0.2", @@ -6678,7 +7440,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", - "dev": true, "requires": { "bn.js": "4.11.6", "strip-hex-prefix": "1.0.0" @@ -6687,22 +7448,24 @@ "bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=", - "dev": true + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" } } }, + "numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY=" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -6729,14 +7492,12 @@ "object-inspect": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", - "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==", - "dev": true + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", @@ -6759,7 +7520,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -6779,6 +7539,88 @@ "has": "^1.0.3" } }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + } + } + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -6903,7 +7745,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", - "dev": true, "requires": { "http-https": "^1.0.0" } @@ -6912,7 +7753,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "dev": true, "requires": { "ee-first": "1.1.1" } @@ -6921,7 +7761,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -6977,14 +7816,12 @@ "p-cancelable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { "version": "1.3.0", @@ -7008,7 +7845,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", - "dev": true, "requires": { "p-finally": "^1.0.0" } @@ -7032,7 +7868,6 @@ "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, "requires": { "asn1.js": "^4.0.0", "browserify-aes": "^1.0.0", @@ -7081,7 +7916,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.2.tgz", "integrity": "sha512-/LypJhzFmyBIDYP9aDVgeyEb5sQfbfY5mnDq4hVhlQ69js87wXfmEI5V3xI6vvXasqebp0oCytYFLxsBVfCzSg==", - "dev": true, "requires": { "for-each": "^0.3.3", "string.prototype.trim": "^1.1.2" @@ -7099,8 +7933,7 @@ "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "pascalcase": { "version": "0.1.1", @@ -7111,14 +7944,12 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-key": { "version": "2.0.1", @@ -7135,8 +7966,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", - "dev": true + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "path-type": { "version": "2.0.0", @@ -7147,11 +7977,15 @@ "pify": "^2.0.0" } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -7169,14 +8003,12 @@ "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { "version": "2.2.2", @@ -7187,20 +8019,17 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -7229,8 +8058,7 @@ "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" }, "preserve": { "version": "0.2.0", @@ -7241,14 +8069,12 @@ "process": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", - "dev": true + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "progress": { "version": "2.0.3", @@ -7269,7 +8095,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", - "dev": true, "requires": { "forwarded": "~0.1.2", "ipaddr.js": "1.9.0" @@ -7284,14 +8109,12 @@ "psl": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", - "dev": true + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", @@ -7304,8 +8127,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -7313,7 +8135,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -7322,20 +8143,17 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", - "dev": true, "requires": { "decode-uri-component": "^0.2.0", "object-assign": "^4.1.0", @@ -7377,7 +8195,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -7386,7 +8203,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, "requires": { "randombytes": "^2.0.5", "safe-buffer": "^5.1.0" @@ -7401,14 +8217,12 @@ "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", - "dev": true, "requires": { "bytes": "3.1.0", "http-errors": "1.7.2", @@ -7441,7 +8255,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -7455,8 +8268,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -7853,7 +8665,6 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -7880,8 +8691,7 @@ "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", - "dev": true + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" } } }, @@ -7908,8 +8718,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "1.0.1", @@ -7942,7 +8751,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", - "dev": true, "requires": { "lowercase-keys": "^1.0.0" } @@ -7982,7 +8790,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, "requires": { "hash-base": "^3.0.0", "inherits": "^2.0.1" @@ -7992,7 +8799,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.4.tgz", "integrity": "sha512-fdq2yYCWpAQBhwkZv+Z8o/Z4sPmYm1CUq6P7n6lVTOdb949CnqA0sndXal5C1NleSVSZm6q5F3iEbauyVln/iw==", - "dev": true, "requires": { "bn.js": "^4.11.1" }, @@ -8000,8 +8806,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" } } }, @@ -8032,8 +8837,7 @@ "safe-buffer": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", - "dev": true + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, "safe-regex": { "version": "1.1.0", @@ -8047,8 +8851,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sc-istanbul": { "version": "0.4.5", @@ -8126,8 +8929,7 @@ "scrypt-js": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", - "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=", - "dev": true + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" }, "scrypt-shim": { "version": "github:web3-js/scrypt-shim#be5e616323a8b5e568788bf94d03c1b8410eac54", @@ -8141,14 +8943,12 @@ "scryptsy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", - "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==", - "dev": true + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" }, "secp256k1": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", - "dev": true, "requires": { "bindings": "^1.5.0", "bip66": "^1.1.5", @@ -8163,14 +8963,12 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "elliptic": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -8184,8 +8982,7 @@ "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" } } }, @@ -8193,7 +8990,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", - "dev": true, "requires": { "commander": "~2.8.1" }, @@ -8202,7 +8998,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true, "requires": { "graceful-readlink": ">= 1.0.0" } @@ -8212,14 +9007,12 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", - "dev": true, "requires": { "debug": "2.6.9", "depd": "~1.1.2", @@ -8240,7 +9033,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" }, @@ -8248,16 +9040,14 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" } } }, @@ -8265,7 +9055,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", - "dev": true, "requires": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", @@ -8277,7 +9066,6 @@ "version": "0.1.12", "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", - "dev": true, "requires": { "body-parser": "^1.16.0", "cors": "^2.8.1", @@ -8289,8 +9077,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "set-value": { "version": "2.0.1", @@ -8318,20 +9105,17 @@ "setimmediate": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", - "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=", - "dev": true + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", - "dev": true + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, "requires": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -8351,7 +9135,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/sha3/-/sha3-1.2.3.tgz", "integrity": "sha512-sOWDZi8cDBRkLfWOw18wvJyNblXDHzwMGnRWut8zNNeIeLnmMRO17bjpLc7OzMuj1ASUgx2IyohzUCAl+Kx5vA==", - "dev": true, "requires": { "nan": "2.13.2" } @@ -8391,14 +9174,12 @@ "simple-concat": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "dev": true + "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" }, "simple-get": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", - "dev": true, "requires": { "decompress-response": "^3.3.0", "once": "^1.3.1", @@ -9204,6 +9985,22 @@ "urix": "^0.1.0" } }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -9254,14 +10051,12 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -9298,8 +10093,7 @@ "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", - "dev": true + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, "stealthy-require": { "version": "1.1.1", @@ -9310,14 +10104,12 @@ "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", "strip-ansi": "^4.0.0" @@ -9327,7 +10119,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, "requires": { "ansi-regex": "^3.0.0" } @@ -9338,7 +10129,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz", "integrity": "sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.13.0", @@ -9349,7 +10139,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz", "integrity": "sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -9359,7 +10148,6 @@ "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -9378,7 +10166,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9388,20 +10175,17 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" }, "is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -9409,14 +10193,12 @@ "object-inspect": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" }, "string.prototype.trimleft": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", @@ -9427,7 +10209,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", @@ -9440,7 +10221,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", - "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -9450,7 +10230,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", - "dev": true, "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" @@ -9460,7 +10239,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz", "integrity": "sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -9470,7 +10248,6 @@ "version": "1.17.5", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -9489,7 +10266,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9499,20 +10275,17 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" }, "is-regex": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, "requires": { "has": "^1.0.3" } @@ -9520,14 +10293,12 @@ "object-inspect": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" }, "string.prototype.trimleft": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", @@ -9538,7 +10309,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5", @@ -9551,7 +10321,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" }, @@ -9559,8 +10328,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -9568,7 +10336,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" }, @@ -9576,8 +10343,7 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" } } }, @@ -9591,7 +10357,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", - "dev": true, "requires": { "is-natural-number": "^4.0.1" } @@ -9606,7 +10371,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", - "dev": true, "requires": { "is-hex-prefixed": "1.0.0" } @@ -9621,7 +10385,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -9630,7 +10393,6 @@ "version": "0.1.39", "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", - "dev": true, "requires": { "bluebird": "^3.5.0", "buffer": "^5.0.5", @@ -9649,14 +10411,12 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "elliptic": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", - "dev": true, "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -9671,7 +10431,6 @@ "version": "0.1.27", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.27.tgz", "integrity": "sha512-B8czsfkJYzn2UIEMwjc7Mbj+Cy72V+/OXH/tb44LV8jhrjizQJJ325xMOMyk3+ETa6r6oi0jsUY14+om8mQMWA==", - "dev": true, "requires": { "bn.js": "^4.11.6", "elliptic": "^6.4.0", @@ -9685,14 +10444,12 @@ "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "got": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", - "dev": true, "requires": { "decompress-response": "^3.2.0", "duplexer3": "^0.1.4", @@ -9713,26 +10470,22 @@ "p-cancelable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", - "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", - "dev": true + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, "requires": { "prepend-http": "^1.0.1" } @@ -9788,7 +10541,6 @@ "version": "4.4.13", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", @@ -9803,7 +10555,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -9814,7 +10565,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "dev": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -9861,14 +10611,12 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, "timers-ext": { "version": "0.1.7", @@ -9892,8 +10640,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", - "dev": true + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, "to-object-path": { "version": "0.3.0", @@ -9907,8 +10654,7 @@ "to-readable-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" }, "to-regex": { "version": "3.0.2", @@ -9946,14 +10692,12 @@ "toidentifier": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", - "dev": true + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -9962,8 +10706,7 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" } } }, @@ -9992,17 +10735,34 @@ "sol-merger": "2.0.1" } }, + "ts-node": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.8.2.tgz", + "integrity": "sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + } + } + }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", - "dev": true + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -10010,14 +10770,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/type/-/type-1.0.3.tgz", - "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==", - "dev": true + "integrity": "sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==" }, "type-check": { "version": "0.3.2", @@ -10028,11 +10786,15 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -10048,11 +10810,15 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, "requires": { "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + }, "uglify-js": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", @@ -10083,14 +10849,12 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", - "dev": true + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" }, "unbzip2-stream": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.0.tgz", "integrity": "sha512-kVx7CDAsdBSWVf404Mw7oI9i09w5/mTT/Ruk+RWa64PLYKvsAucLLFHvQtnvjeADM4ZizxrvG5SHnF4Te4T2Cg==", - "dev": true, "requires": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -10099,8 +10863,7 @@ "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==", - "dev": true + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "union-value": { "version": "1.0.1", @@ -10117,14 +10880,12 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", - "dev": true + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "unset-value": { "version": "1.0.0", @@ -10176,7 +10937,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" } @@ -10191,7 +10951,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, "requires": { "prepend-http": "^2.0.0" } @@ -10199,14 +10958,12 @@ "url-set-query": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", - "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=", - "dev": true + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" }, "url-to-options": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", - "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", - "dev": true + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" }, "use": { "version": "3.1.1", @@ -10217,26 +10974,22 @@ "utf8": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "dev": true + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "dev": true + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", - "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=", - "dev": true + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" }, "v8-compile-cache": { "version": "2.1.0", @@ -10257,14 +11010,12 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", - "dev": true + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -10669,11 +11420,15 @@ } } }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -10681,8 +11436,15 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } }, "word-wrap": { "version": "1.2.3", @@ -10746,8 +11508,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -10773,7 +11534,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "dev": true, "requires": { "async-limiter": "~1.0.0", "safe-buffer": "~5.1.0", @@ -10783,8 +11543,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, @@ -10792,7 +11551,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", - "dev": true, "requires": { "global": "~4.3.0", "is-function": "^1.0.1", @@ -10804,7 +11562,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", - "dev": true, "requires": { "buffer-to-arraybuffer": "^0.0.5", "object-assign": "^4.1.1", @@ -10819,7 +11576,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.2.tgz", "integrity": "sha1-NDxE0e53JrhkgGloLQ+EDIO0Jh0=", - "dev": true, "requires": { "xhr-request": "^1.0.1" } @@ -10828,7 +11584,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", - "dev": true, "requires": { "cookiejar": "^2.1.1" } @@ -10836,32 +11591,27 @@ "xmlhttprequest": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=", - "dev": true + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" }, "yaeti": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=", - "dev": true + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { "version": "10.1.2", @@ -10908,15 +11658,135 @@ } } }, + "yargs-unparser": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", + "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.15", + "yargs": "^13.3.0" + }, + "dependencies": { + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", - "dev": true, "requires": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" } } } diff --git a/solidity/package.json b/solidity/package.json index 4a295ae0..42b97b8b 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -13,6 +13,7 @@ "author": "James Prestwich", "license": "UNLICENSED", "dependencies": { + "@celo/contractkit": "^0.3.3", "@summa-tx/bitcoin-spv-sol": "^2.2.0", "bn.js": "^5.1.1", "dotenv": "^8.2.0" diff --git a/solidity/truffle-config.js b/solidity/truffle-config.js index 66bad2a7..9d502588 100644 --- a/solidity/truffle-config.js +++ b/solidity/truffle-config.js @@ -1,9 +1,13 @@ /* eslint-disable */ require('dotenv').config(); + +const Kit = require('@celo/contractkit') + const HDWalletProvider = require('truffle-hdwallet-provider'); const infuraKey = process.env.SUMMA_RELAY_INFURA_KEY; const mnemonic = process.env.MNEMONIC; + const ropsten = { provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/${infuraKey}`), network_id: 3, @@ -21,12 +25,21 @@ const kovan = { } const alfajores = { - host: process.env.ALFAJORES_NODE_URL, + provider: () => { + const provider = new HDWalletProvider(mnemonic, 'http://127.0.0.1:9999'); // sinkhole any requests + // slip44 + const celoBIP44 = "m/44'/52752'/0'/0/0"; + const hdkey = provider.hdwallet.derivePath(celoBIP44); + // Get the privkey and hand it to the kit + const privkey = hdkey._hdkey.privateKey.toString('hex'); + const kit = Kit.newKit('https://alfajores-forno.celo-testnet.org'); + kit.addAccount(privkey); + return kit.web3.currentProvider; + }, network_id: 44786, - port: process.env.ALFAJORES_NODE_PORT, - from: process.env.ALFAJORES_FROM, - gas: 8000000, - gasPrice: 100000000000 + gas: 5500000, + confirmations: 2, + timeoutBlocks: 200 } module.exports = { From 3b51c929fdb88ac0a4c4c447f7baf085445d641e Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Mon, 13 Apr 2020 14:25:29 -0600 Subject: [PATCH 092/148] update btcspv in go (#60) --- golang/go.mod | 2 +- golang/go.sum | 2 + golang/x/relay/keeper/chain.go | 12 +- golang/x/relay/keeper/chain_test.go | 20 +- golang/x/relay/keeper/handler_test.go | 12 +- golang/x/relay/keeper/headers.go | 6 +- golang/x/relay/keeper/headers_test.go | 12 +- golang/x/relay/keeper/keeper.go | 6 +- golang/x/relay/keeper/keeper_test.go | 6 +- golang/x/relay/keeper/links.go | 6 +- golang/x/relay/keeper/links_test.go | 6 +- golang/x/relay/keeper/querier_test.go | 54 +- golang/x/relay/keeper/validator.go | 4 +- golang/x/relay/keeper/validator_test.go | 4 +- golang/x/relay/types/events.go | 16 +- testVectors.json | 870 ++++++++---------------- 16 files changed, 375 insertions(+), 663 deletions(-) diff --git a/golang/go.mod b/golang/go.mod index 1f6fe35b..1a854840 100644 --- a/golang/go.mod +++ b/golang/go.mod @@ -20,7 +20,7 @@ require ( github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.5.0 github.com/stretchr/testify v1.4.0 - github.com/summa-tx/bitcoin-spv/golang v1.3.0 + github.com/summa-tx/bitcoin-spv/golang v1.4.0 github.com/tendermint/go-amino v0.15.0 github.com/tendermint/tendermint v0.32.2 github.com/tendermint/tm-db v0.1.1 diff --git a/golang/go.sum b/golang/go.sum index 707a8dbb..1dc3a02e 100644 --- a/golang/go.sum +++ b/golang/go.sum @@ -369,6 +369,8 @@ github.com/summa-tx/bitcoin-spv/golang v1.2.0 h1:d9VSHSb4HMQOYshbOTRh26g5yU4tUiB github.com/summa-tx/bitcoin-spv/golang v1.2.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/summa-tx/bitcoin-spv/golang v1.3.0 h1:BHs+LZsCYd/UhHssiGuj6TZ3h8eBytIOVtiBTfVOiLc= github.com/summa-tx/bitcoin-spv/golang v1.3.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= +github.com/summa-tx/bitcoin-spv/golang v1.4.0 h1:LQ/AWlf/jbkivtvZtEot3/nzUt4/Ll8/9IXOtNN0xAs= +github.com/summa-tx/bitcoin-spv/golang v1.4.0/go.mod h1:8VS5yAT8po0RU1YM86UIr1klFm0/JBf5bZHL0eHpYLs= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3 h1:sAlSBRDl4psFR3ysKXRSE8ss6Mt90+ma1zRTroTNBJA= github.com/syndtr/goleveldb v0.0.0-20180708030551-c4c61651e9e3/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20190318030020-c3a204f8e965 h1:1oFLiOyVl+W7bnBzGhf7BbIv9loSFQcieWWYIjLqcAw= diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 51f75f48..6438cd0f 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -119,16 +119,16 @@ func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, new 4. They're in different new windows. Choose the heavier one */ if !leftInPeriod && rightInPeriod { - return leftBlock.HashLE, nil + return leftBlock.Hash, nil } if leftInPeriod && !rightInPeriod { - return rightBlock.HashLE, nil + return rightBlock.Hash, nil } if leftInPeriod && rightInPeriod { if leftBlock.Height >= rightBlock.Height { - return leftBlock.HashLE, nil + return leftBlock.Hash, nil } - return rightBlock.HashLE, nil + return rightBlock.Hash, nil } // if !leftInPeriod && !rightInPeriod @@ -139,9 +139,9 @@ func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, new rightAccDiff := rightDiff.Mul(sdk.NewUint(uint64(rightBlock.Height % 2016))) if leftAccDiff.GTE(rightAccDiff) { - return leftBlock.HashLE, nil + return leftBlock.Hash, nil } - return rightBlock.HashLE, nil + return rightBlock.Hash, nil } // MarkNewHeaviest updates the best known digest and LCA diff --git a/golang/x/relay/keeper/chain_test.go b/golang/x/relay/keeper/chain_test.go index ed402d20..048043c1 100644 --- a/golang/x/relay/keeper/chain_test.go +++ b/golang/x/relay/keeper/chain_test.go @@ -9,7 +9,7 @@ import ( func (s *KeeperSuite) TestEmitReorg() { headers := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers - s.Keeper.emitReorg(s.Context, headers[0].HashLE, headers[1].HashLE, headers[2].HashLE) + s.Keeper.emitReorg(s.Context, headers[0].Hash, headers[1].Hash, headers[2].Hash) events := s.Context.EventManager().Events() e := events[0] @@ -28,14 +28,14 @@ func (s *KeeperSuite) TestGetDigestByStoreKey() { } func (s *KeeperSuite) TestGetBestKnownDigest() { - digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].HashLE + digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].Hash s.Keeper.setBestKnownDigest(s.Context, digest) bestKnown, _ := s.Keeper.GetBestKnownDigest(s.Context) s.Equal(digest, bestKnown) } func (s *KeeperSuite) TestGetLastReorgLCA() { - digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].HashLE + digest := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0].Hash s.Keeper.setLastReorgLCA(s.Context, digest) lca, _ := s.Keeper.GetLastReorgLCA(s.Context) s.Equal(digest, lca) @@ -55,9 +55,9 @@ func (s *KeeperSuite) TestIsMostRecentCommonAncestor() { err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, post) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, postWithOrphan) s.SDKNil(err) for i := range tv.TestCases { @@ -116,7 +116,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { err = s.Keeper.MarkNewHeaviest( s.Context, - tv.Genesis.HashLE, + tv.Genesis.Hash, pre[0].Raw, pre[1].Raw, 10, @@ -125,15 +125,15 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, post) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, postWithOrphan) s.SDKNil(err) // errors if the ancestor is not the heaviest common ancestor err = s.Keeper.MarkNewHeaviest( s.Context, - tv.Genesis.HashLE, + tv.Genesis.Hash, tv.Genesis.Raw, pre[0].Raw, 10, @@ -141,7 +141,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { s.SDKNil(err) err = s.Keeper.MarkNewHeaviest( s.Context, - tv.Genesis.HashLE, + tv.Genesis.Hash, pre[0].Raw, pre[1].Raw, 10, diff --git a/golang/x/relay/keeper/handler_test.go b/golang/x/relay/keeper/handler_test.go index 7fee1841..ab586d32 100644 --- a/golang/x/relay/keeper/handler_test.go +++ b/golang/x/relay/keeper/handler_test.go @@ -56,7 +56,7 @@ func (s *KeeperSuite) TestHandleMsgIngestDifficultyChange() { testCases := s.Fixtures.HeaderTestCases.ValidateDiffChange handler := NewHandler(s.Keeper) - newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) + newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.Hash, testCases[0].Headers) res := handler(s.Context, newMsg) s.Equal(sdk.CodeType(103), res.Code) @@ -73,7 +73,7 @@ func (s *KeeperSuite) TestHandleMsgMarkNewHeaviest() { s.Keeper.ingestHeader(s.Context, testCases[0].PrevEpochStart) s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) - newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.HashLE, testCases[0].Headers) + newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.Hash, testCases[0].Headers) res := handler(s.Context, newMsg) s.Equal("extension", res.Events[0].Type) } @@ -93,18 +93,18 @@ func (s *KeeperSuite) TestHandleMarkNewHeaviest() { err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, post) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, postWithOrphan) s.SDKNil(err) // returns correct error - newMsg := types.NewMsgMarkNewHeaviest(getAccAddress(), tv.OldPeriodStart.HashLE, tv.OldPeriodStart.Raw, tv.OldPeriodStart.Raw, 10) + newMsg := types.NewMsgMarkNewHeaviest(getAccAddress(), tv.OldPeriodStart.Hash, tv.OldPeriodStart.Raw, tv.OldPeriodStart.Raw, 10) res := handler(s.Context, newMsg) s.Equal(sdk.CodeType(403), res.Code) // Successfully marks new heaviest - newMsg = types.NewMsgMarkNewHeaviest(getAccAddress(), tv.Genesis.HashLE, tv.Genesis.Raw, pre[0].Raw, 10) + newMsg = types.NewMsgMarkNewHeaviest(getAccAddress(), tv.Genesis.Hash, tv.Genesis.Raw, pre[0].Raw, 10) res = handler(s.Context, newMsg) s.Equal("extension", res.Events[0].Type) } diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index e36e66c3..e8a6be61 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -115,7 +115,7 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store := k.getHeaderStore(ctx) buf := k.cdc.MustMarshalBinaryBare(header) - store.Set(header.HashLE[:], buf) + store.Set(header.Hash[:], buf) } func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHeader, internal, isMainnet bool) sdk.Error { @@ -164,7 +164,7 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead } func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { - anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) + anchor, err := k.GetHeader(ctx, headers[0].PrevHash) if err != nil { return err } @@ -217,7 +217,7 @@ func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.H if err != nil { return err } - anchor, err := k.GetHeader(ctx, headers[0].PrevHashLE) + anchor, err := k.GetHeader(ctx, headers[0].PrevHash) if err != nil { return err } diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 3ce3eb29..287b357f 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -8,7 +8,7 @@ import ( func (s *KeeperSuite) TestGetHeader() { // errors if header is not found header := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0] - _, err := s.Keeper.GetHeader(s.Context, header.HashLE) + _, err := s.Keeper.GetHeader(s.Context, header.Hash) s.Equal(types.CodeType(103), err.Code()) } @@ -83,9 +83,9 @@ func (s *KeeperSuite) TestIngestHeader() { for _, tc := range cases { s.Keeper.ingestHeader(s.Context, tc.Headers[0]) - hasHeader := s.Keeper.HasHeader(s.Context, tc.Headers[0].HashLE) + hasHeader := s.Keeper.HasHeader(s.Context, tc.Headers[0].Hash) s.Equal(true, hasHeader) - header, err := s.Keeper.GetHeader(s.Context, tc.Headers[0].HashLE) + header, err := s.Keeper.GetHeader(s.Context, tc.Headers[0].Hash) s.SDKNil(err) s.Equal(tc.Headers[0], header) } @@ -110,18 +110,18 @@ func (s *KeeperSuite) TestIngestDifficultyChange() { cases := s.Fixtures.HeaderTestCases.ValidateDiffChange // errors if PrevEpochStart is not found - err := s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.HashLE, cases[0].Headers) + err := s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.Hash, cases[0].Headers) s.Equal(types.CodeType(103), err.Code()) // errors if anchor is not found s.Keeper.ingestHeader(s.Context, cases[0].PrevEpochStart) - err = s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.HashLE, cases[0].Headers) + err = s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.Hash, cases[0].Headers) s.Equal(types.CodeType(103), err.Code()) for _, tc := range cases { s.Keeper.ingestHeader(s.Context, tc.PrevEpochStart) s.Keeper.ingestHeader(s.Context, tc.Anchor) - err := s.Keeper.IngestDifficultyChange(s.Context, tc.PrevEpochStart.HashLE, tc.Headers) + err := s.Keeper.IngestDifficultyChange(s.Context, tc.PrevEpochStart.Hash, tc.Headers) if tc.Output == 0 { logIfTestCaseError(tc, err) s.SDKNil(err) diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index 7e06c6c7..ab6199d1 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -53,9 +53,9 @@ func (k Keeper) SetGenesisState(ctx sdk.Context, genesis, epochStart btcspv.Bitc k.ingestHeader(ctx, genesis) k.ingestHeader(ctx, epochStart) - k.setRelayGenesis(ctx, genesis.HashLE) - k.setBestKnownDigest(ctx, genesis.HashLE) - k.setLastReorgLCA(ctx, genesis.HashLE) + k.setRelayGenesis(ctx, genesis.Hash) + k.setBestKnownDigest(ctx, genesis.Hash) + k.setLastReorgLCA(ctx, genesis.Hash) // this will only fail if the genesis state is corrupt _ = k.setCurrentEpochDifficulty(ctx, btcspv.ExtractDifficulty(genesis.Raw)) diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index c88740f4..748dc74d 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -299,15 +299,15 @@ func (s *KeeperSuite) TestSetGenesisState() { gen, err := s.Keeper.GetRelayGenesis(s.Context) s.SDKNil(err) - s.Equal(genesis.HashLE, gen) + s.Equal(genesis.Hash, gen) lca, err := s.Keeper.GetLastReorgLCA(s.Context) s.SDKNil(err) - s.Equal(genesis.HashLE, lca) + s.Equal(genesis.Hash, lca) best, err := s.Keeper.GetBestKnownDigest(s.Context) s.SDKNil(err) - s.Equal(genesis.HashLE, best) + s.Equal(genesis.Hash, best) diff := s.Keeper.getCurrentEpochDifficulty(s.Context) s.Equal(btcspv.ExtractDifficulty(genesis.Raw), diff) diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index 017a90d0..c90d406f 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -18,15 +18,15 @@ func (k Keeper) hasLink(ctx sdk.Context, digestLE types.Hash256Digest) bool { func (k Keeper) setLink(ctx sdk.Context, header types.BitcoinHeader) { store := k.getLinkStore(ctx) - store.Set(header.HashLE[:], header.PrevHashLE[:]) + store.Set(header.Hash[:], header.PrevHash[:]) } func (k Keeper) getLink(ctx sdk.Context, digestLE types.Hash256Digest) types.Hash256Digest { store := k.getLinkStore(ctx) buf := store.Get(digestLE[:]) // Can only fail if data store is corrupt - parentHashLE, _ := btcspv.NewHash256Digest(buf) - return parentHashLE + parentHash, _ := btcspv.NewHash256Digest(buf) + return parentHash } // FindAncestor finds the nth ancestor of some digest diff --git a/golang/x/relay/keeper/links_test.go b/golang/x/relay/keeper/links_test.go index fba187ce..275f0327 100644 --- a/golang/x/relay/keeper/links_test.go +++ b/golang/x/relay/keeper/links_test.go @@ -9,10 +9,10 @@ func (s *KeeperSuite) TestGetLink() { // stores and retrieves link s.Keeper.setLink(s.Context, child) - hasHeader := s.Keeper.hasLink(s.Context, child.HashLE) + hasHeader := s.Keeper.hasLink(s.Context, child.Hash) s.Equal(true, hasHeader) - getHeader := s.Keeper.getLink(s.Context, child.HashLE) - s.Equal(parent.HashLE, getHeader) + getHeader := s.Keeper.getLink(s.Context, child.Hash) + s.Equal(parent.Hash, getHeader) } func (s *KeeperSuite) TestFindAncestor() { diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index 66e9e881..0f0e87ae 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -85,8 +85,8 @@ func (s *KeeperSuite) TestQueryIsAncestor() { s.SDKNil(err) params := types.QueryParamsIsAncestor{ - DigestLE: headers[4].HashLE, - ProspectiveAncestor: headers[1].HashLE, + DigestLE: headers[4].Hash, + ProspectiveAncestor: headers[1].Hash, Limit: 15, } marshalledParams, marshalErr := json.Marshal(params) @@ -110,8 +110,8 @@ func (s *KeeperSuite) TestQueryIsAncestor() { // If Limit is 0, it will use default limit params = types.QueryParamsIsAncestor{ - DigestLE: headers[4].HashLE, - ProspectiveAncestor: headers[1].HashLE, + DigestLE: headers[4].Hash, + ProspectiveAncestor: headers[1].Hash, Limit: 0, } marshalledParams, marshalErr = json.Marshal(params) @@ -168,7 +168,7 @@ func (s *KeeperSuite) TestQueryGetRelayGenesis() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(genesis.HashLE, result.Res) + s.Equal(genesis.Hash, result.Res) } func (s *KeeperSuite) TestQueryGetLastReorgLCA() { @@ -197,7 +197,7 @@ func (s *KeeperSuite) TestQueryGetLastReorgLCA() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(genesis.HashLE, result.Res) + s.Equal(genesis.Hash, result.Res) } func (s *KeeperSuite) TestQueryFindAncestor() { @@ -206,7 +206,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { querier := NewQuerier(s.Keeper) params := types.QueryParamsFindAncestor{ - DigestLE: headers[4].HashLE, + DigestLE: headers[4].Hash, Offset: 2, } marshalledParams, marshalErr := json.Marshal(params) @@ -236,7 +236,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) // s.SDKNil(unmarshallErr) s.Nil(unmarshallErr) - s.Equal(headers[2].HashLE, result.Res) + s.Equal(headers[2].Hash, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -265,9 +265,9 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { s.SDKNil(err) params := types.QueryParamsHeaviestFromAncestor{ - Ancestor: headers[3].HashLE, - CurrentBest: headers[5].HashLE, - NewBest: headers[4].HashLE, + Ancestor: headers[3].Hash, + CurrentBest: headers[5].Hash, + NewBest: headers[4].Hash, Limit: 20, } marshalledParams, marshalErr := json.Marshal(params) @@ -287,13 +287,13 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { unmarshallErr := types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(headers[5].HashLE, result.Res) + s.Equal(headers[5].Hash, result.Res) // Test that it errors if HeaviestFromAncestorErrors params = types.QueryParamsHeaviestFromAncestor{ - Ancestor: tv.Headers[10].HashLE, - CurrentBest: headers[3].HashLE, - NewBest: headers[4].HashLE, + Ancestor: tv.Headers[10].Hash, + CurrentBest: headers[3].Hash, + NewBest: headers[4].Hash, Limit: 20, } marshalledParams, marshalErr = json.Marshal(params) @@ -309,9 +309,9 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { // Test that default limit is used if limit is set to zero params = types.QueryParamsHeaviestFromAncestor{ - Ancestor: headers[3].HashLE, - CurrentBest: headers[5].HashLE, - NewBest: headers[4].HashLE, + Ancestor: headers[3].Hash, + CurrentBest: headers[5].Hash, + NewBest: headers[4].Hash, Limit: 0, } marshalledParams, marshalErr = json.Marshal(params) @@ -327,7 +327,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { unmarshallErr = types.ModuleCdc.UnmarshalJSON(res, &result) s.Nil(unmarshallErr) - s.Equal(headers[5].HashLE, result.Res) + s.Equal(headers[5].Hash, result.Res) // Test unmarshall error req = abci.RequestQuery{ @@ -354,15 +354,15 @@ func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, post) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, post) s.SDKNil(err) - err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.HashLE, postWithOrphan) + err = s.Keeper.IngestDifficultyChange(s.Context, tv.OldPeriodStart.Hash, postWithOrphan) s.SDKNil(err) params := types.QueryParamsIsMostRecentCommonAncestor{ - Ancestor: post[2].HashLE, - Left: post[3].HashLE, - Right: post[2].HashLE, + Ancestor: post[2].Hash, + Left: post[3].Hash, + Right: post[2].Hash, Limit: 5, } marshalledParams, marshalErr := json.Marshal(params) @@ -386,9 +386,9 @@ func (s *KeeperSuite) TestQueryIsMostRecentCommonAncestor() { // Test that it looks up the default limit if limit is set to zero params = types.QueryParamsIsMostRecentCommonAncestor{ - Ancestor: post[2].HashLE, - Left: post[3].HashLE, - Right: post[2].HashLE, + Ancestor: post[2].Hash, + Left: post[3].Hash, + Right: post[2].Hash, Limit: 0, } marshalledParams, marshalErr = json.Marshal(params) diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go index 43ef3eb3..6ccef6cf 100644 --- a/golang/x/relay/keeper/validator.go +++ b/golang/x/relay/keeper/validator.go @@ -13,7 +13,7 @@ func (k Keeper) emitProofProvided( for _, f := range filled.Filled { filledIDs = append(filledIDs, f.ID) } - ctx.EventManager().EmitEvent(types.NewProofProvidedEvent(filled.Proof.TxIDLE, filledIDs)) + ctx.EventManager().EmitEvent(types.NewProofProvidedEvent(filled.Proof.TxID, filledIDs)) } func (k Keeper) getConfs(ctx sdk.Context, header types.BitcoinHeader) (uint32, sdk.Error) { @@ -39,7 +39,7 @@ func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { if lcaErr != nil { return lcaErr } - isAncestor := k.IsAncestor(ctx, proof.ConfirmingHeader.HashLE, lca, 240) + isAncestor := k.IsAncestor(ctx, proof.ConfirmingHeader.Hash, lca, 240) if !isAncestor { return types.ErrNotAncestor(types.DefaultCodespace) } diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go index 7ea118bf..e8f33707 100644 --- a/golang/x/relay/keeper/validator_test.go +++ b/golang/x/relay/keeper/validator_test.go @@ -15,7 +15,7 @@ func (s *KeeperSuite) TestGetConfs() { s.Equal(uint32(0), confs) // errors if Best Known Digest header is not found - s.Keeper.setBestKnownDigest(s.Context, bestKnown.HashLE) + s.Keeper.setBestKnownDigest(s.Context, bestKnown.Hash) confs, err = s.Keeper.getConfs(s.Context, header) s.Equal(sdk.CodeType(103), err.Code()) @@ -74,7 +74,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { err := s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) s.Equal(sdk.CodeType(105), err.Code()) - s.Keeper.setBestKnownDigest(s.Context, validProof.BestKnown.HashLE) + s.Keeper.setBestKnownDigest(s.Context, validProof.BestKnown.Hash) // errors if checkRequest errors // deactivate request diff --git a/golang/x/relay/types/events.go b/golang/x/relay/types/events.go index a3eab88b..3d58a150 100644 --- a/golang/x/relay/types/events.go +++ b/golang/x/relay/types/events.go @@ -1,8 +1,8 @@ package types import ( - "encoding/json" "encoding/hex" + "encoding/json" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" @@ -10,9 +10,9 @@ import ( // Relay module event types const ( - EventTypeExtension = "extension" - EventTypeReorg = "reorg" - EventTypeProofRequest = "proof_request" + EventTypeExtension = "extension" + EventTypeReorg = "reorg" + EventTypeProofRequest = "proof_request" EventTypeProofProvided = "proof_provided" AttributeKeyFirstBlock = "first_block" @@ -28,7 +28,7 @@ const ( AttributeKeyPaysValue = "value" AttributeKeyOrigin = "origin" - AttributeKeyTXID = "txid" + AttributeKeyTXID = "txid" AttributeKeyFilled = "filled" ) @@ -46,8 +46,8 @@ func NewReorgEvent(prev, new, lca Hash256Digest) sdk.Event { func NewExtensionEvent(first, last BitcoinHeader) sdk.Event { return sdk.NewEvent( EventTypeExtension, - sdk.NewAttribute(AttributeKeyFirstBlock, "0x"+hex.EncodeToString(first.HashLE[:])), - sdk.NewAttribute(AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.HashLE[:])), + sdk.NewAttribute(AttributeKeyFirstBlock, "0x"+hex.EncodeToString(first.Hash[:])), + sdk.NewAttribute(AttributeKeyLastBlock, "0x"+hex.EncodeToString(last.Hash[:])), ) } @@ -68,7 +68,7 @@ func NewProofProvidedEvent(txid Hash256Digest, filled []RequestID) sdk.Event { filledJSON, _ := json.Marshal(filled) return sdk.NewEvent( EventTypeProofProvided, - sdk.NewAttribute(AttributeKeyTXID, "0x" + hex.EncodeToString(txid[:])), + sdk.NewAttribute(AttributeKeyTXID, "0x"+hex.EncodeToString(txid[:])), sdk.NewAttribute(AttributeKeyFilled, string(filledJSON)), ) } diff --git a/testVectors.json b/testVectors.json index ad44da33..9baee314 100644 --- a/testVectors.json +++ b/testVectors.json @@ -44,44 +44,32 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" }, { "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", - "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", - "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "hash": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", "height": 306433, - "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", - "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", - "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + "prevhash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" } ], "prevEpochStart": { "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", - "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", - "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "hash": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", "height": 304416, - "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", - "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", - "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", - "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + "prevhash": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" }, "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 0 }, @@ -90,34 +78,25 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" } ], "prevEpochStart": { "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", - "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", - "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "hash": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", "height": 304416, - "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", - "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", - "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", - "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + "prevhash": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" }, "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306432, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 301 }, @@ -126,34 +105,25 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" } ], "prevEpochStart": { "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", - "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", - "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "hash": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", "height": 304417, - "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", - "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", - "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", - "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + "prevhash": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" }, "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 302 }, @@ -162,34 +132,25 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" } ], "prevEpochStart": { "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690538a855d18b189b5e4", - "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", - "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "hash": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", "height": 304416, - "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", - "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", - "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", - "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + "prevhash": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" }, "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 303 }, @@ -198,44 +159,32 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5218824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" }, { "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", - "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", - "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "hash": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", "height": 306433, - "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", - "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", - "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + "prevhash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" } ], "prevEpochStart": { "raw": "02000000dca825543cefd662b3199e02afa13c6aad4b01890180010400000000000000000187743d481db520170f22701f34a1449384446a55575e2c46e183de28b4854701e690539a855d18b189b5e4", - "hash": "00000000000000000ead30f2a70f979a6b07f841ca05302497a92b3c9a9488a3", - "hash_le": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", + "hash": "a388949a3c2ba997243005ca41f8076b9a970fa7f230ad0e0000000000000000", "height": 304416, - "prevhash": "00000000000000000401800189014bad6a3ca1af029e19b362d6ef3c5425a8dc", - "prevhash_le": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", - "merkle_root": "4785b428de83e1462c5e57556a44849344a1341f70220f1720b51d483d748701", - "merkle_root_le": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" + "prevhash": "dca825543cefd662b3199e02afa13c6aad4b0189018001040000000000000000", + "merkle_root": "0187743d481db520170f22701f34a1449384446a55575e2c46e183de28b48547" }, "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "output": 304 } @@ -246,183 +195,129 @@ "headers": [ { "height": 562623, - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" }, { "height": 562624, - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" }, { "height": 562625, - "hash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", - "hash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", - "prevhash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "prevhash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "merkle_root": "0x14a87c0e6d701c62e4cb4f3034f55bcbfd670657aa853e9b46ef9524c16582e7", - "merkle_root_le": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814", + "hash": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "prevhash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "merkle_root": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814", "raw": "0x00e0ff3f8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000e78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca8149dc3615c886f2e171c3fa2b8" }, { "height": 562626, - "hash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", - "hash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", - "prevhash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", - "prevhash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", - "merkle_root": "0xda33c59bc6f3c950d8bc9be1779b0c03ddc67f7ab9304fb62cd456a1840b516f", - "merkle_root_le": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da", + "hash": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "prevhash": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "merkle_root": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da", "raw": "0x00000020fee20039fe494f7408c090e03970ec9b132366066f380d0000000000000000006f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533dacec3615c886f2e170a1a921d" }, { "height": 562627, - "hash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", - "hash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", - "prevhash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", - "prevhash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", - "merkle_root": "0x20ead5553be95c71e87839492197ea48ccf87dbeb0b25ed4bc4e97bb0214a36a", - "merkle_root_le": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20", + "hash": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "prevhash": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "merkle_root": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20", "raw": "0x00000020d2406bb15e4f917104d2b2d9320454b947fb08a723301d0000000000000000006aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20d1c5615c886f2e1767973d73" }, { "height": 562628, - "hash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", - "hash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", - "prevhash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", - "prevhash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", - "merkle_root": "0xe9ab8cb51b64c924b61c52f8be389955c983ae2315ee042dc76cab3b3a338824", - "merkle_root_le": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9", + "hash": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "prevhash": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "merkle_root": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9", "raw": "0x00e0002096f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b0000000000000000002488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe953ce615c886f2e172e0885ce" }, { "height": 562629, - "hash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "hash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", - "prevhash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", - "prevhash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", - "merkle_root": "0x7f8d7afb20451eb0dfd026ecc2f71e9f126fc536939fe3de6ed324d36f0a2ee7", - "merkle_root_le": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f", + "hash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "prevhash": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "merkle_root": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f", "raw": "0x00000020b86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000e72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7fcdce615c886f2e17415cdb46" }, { "height": 562630, - "hash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", - "hash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", - "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", - "merkle_root": "0xec8294c76e141d9244c6fec847fea8dbbb529933b279a2102a61da7f5179149d", - "merkle_root_le": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec", + "hash": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "prevhash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec", "raw": "0x0000002020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f050000000000000000009d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ecebd3615c886f2e179234c38e" }, { "height": 562631, - "hash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", - "hash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", - "prevhash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", - "prevhash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", - "merkle_root": "0x31b36a8983956a484b5ba867fa9cd7e70ae0b4c78dcbe6da1353eb564744af00", - "merkle_root_le": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331", + "hash": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "prevhash": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "merkle_root": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331", "raw": "0x0000002051214b0c42383a1ea7bf28f20062f81d7b72497cb1030a00000000000000000000af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab3314bd8615c886f2e17f152bc1f" }, { "height": 562632, - "hash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", - "hash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", - "prevhash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", - "prevhash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", - "merkle_root": "0xd029d1fadb3f9e673ff5155d227918c860962dcc1999bb77d6e2d655b5fcc2b2", - "merkle_root_le": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0", + "hash": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "prevhash": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "merkle_root": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0", "raw": "0x00e00020bf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000b2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d032db615c886f2e17155f22df" }, { "height": 562633, - "hash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", - "hash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", - "prevhash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", - "prevhash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", - "merkle_root": "0x351f74fe8fab94e161ce4f9d2f2da2cac1b7af93abe7054a1d37635f5cf7334e", - "merkle_root_le": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35", + "hash": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "prevhash": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "merkle_root": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35", "raw": "0x00000020a4c292016c1585e6f81986f7e216c79d28b15b1d513a100000000000000000004e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35c0de615c886f2e17032ddf4b" }, { "height": 562634, - "hash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", - "hash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", - "prevhash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", - "prevhash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", - "merkle_root": "0x80214eef007bb437affecc7279cd81fa215e40096f47622d90e19ab0520d20ef", - "merkle_root_le": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180", + "hash": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "prevhash": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "merkle_root": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180", "raw": "0x00000020577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000ef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180cdde615c886f2e171f2b5f80" }, { "height": 562635, - "hash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", - "hash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", - "prevhash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", - "prevhash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", - "merkle_root": "0x9c58834eb6b069f57886efe06fe2af4be50cf82446160609e728867bfc3817a9", - "merkle_root_le": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c", + "hash": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "prevhash": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "merkle_root": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c", "raw": "0x00000020f5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000a91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589cb3e0615c886f2e17c624e58c" }, { "height": 562636, - "hash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", - "hash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", - "prevhash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", - "prevhash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", - "merkle_root": "0xc2c7bcd4be5c099df57632870ccf7cc345df60d46576197321ac90fef0149bc2", - "merkle_root_le": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2", + "hash": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "prevhash": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "merkle_root": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2", "raw": "0x00e00020f98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000c29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2fae4615c886f2e178a505d9d" }, { "height": 562637, - "hash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", - "hash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", - "prevhash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", - "prevhash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", - "merkle_root": "0x650d7ffc89ce1160ed30f83a2c0bbe807f289987076e19f1916f861929700535", - "merkle_root_le": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65", + "hash": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "prevhash": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "merkle_root": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65", "raw": "0x000000200ed18ffcb751e45471dddab23d34538869d3b2cdd484280000000000000000003505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d657ce5615c886f2e17d759c936" }, { "height": 562638, - "hash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", - "hash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", - "prevhash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", - "prevhash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", - "merkle_root": "0x8258a466e5628d96ced147e5e18955923699b2daad38be299e42490880327a09", - "merkle_root_le": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882", + "hash": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "prevhash": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "merkle_root": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882", "raw": "0x0000802030ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882f1e5615c886f2e1796e65983" }, { "height": 562639, - "hash": "0x0000000000000000000fb8dba7f48eb65dbc24c6dc03d6f84823b5d59c7c4148", - "hash_le": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", - "prevhash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", - "prevhash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", - "merkle_root": "0xe9bedf6cd3fe3df1a7ba1d194d42a5caca6a3b323ed28270c2db2ff72d698818", - "merkle_root_le": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9", + "hash": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", + "prevhash": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "merkle_root": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9", "raw": "0x0000802051f32093b60f4484c041d383fb6bf35c44d428bde54d2d0000000000000000001888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee905e6615c886f2e177b59d28f" } ], "anchor": { "height": 562622, - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" }, "internal": false, @@ -434,33 +329,24 @@ "headers": [ { "height": 562623, - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" }, { "height": 562624, - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" } ], "anchor": { "height": 562622, - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" }, "internal": true, @@ -472,34 +358,25 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" }, { "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", - "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", - "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "hash": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", "height": 306433, - "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", - "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", - "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + "prevhash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" } ], "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "internal": false, "isMainnet": false, @@ -510,34 +387,25 @@ "headers": [ { "raw": "02000000b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b97642004a153a2ab5118824d1fac", - "hash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "hash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "hash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", "height": 306432, - "prevhash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "prevhash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", - "merkle_root": "64972bd3fbb34cc2fb37afa5f14d362f1190d122525c4d604af576ea68b319cd", - "merkle_root_le": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" + "prevhash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "merkle_root": "cd19b368ea76f54a604d5c5222d190112f364df1a5af37fbc24cb3fbd32b9764" }, { "raw": "02000000e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7e50aa153a2ab51182fa596fc", - "hash": "000000000000000000d415c6272a2feeac951a4e51a3d7178569a5296d7add8a", - "hash_le": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", + "hash": "8add7a6d29a5698517d7a3514e1a95acee2f2a27c615d4000000000000000000", "height": 306433, - "prevhash": "00000000000000000d03d0dc77d39f1657e14f9d66ba9b46fb5a9562aaa1c9e9", - "prevhash_le": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", - "merkle_root": "f775af086c515a63ffa3d722e6721738f0d060e48c3d98310b668d0ea0208986", - "merkle_root_le": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" + "prevhash": "e9c9a1aa62955afb469bba669d4fe157169fd377dcd0030d0000000000000000", + "merkle_root": "868920a00e8d660b31983d8ce460d0f0381772e622d7a3ff635a516c08af75f7" } ], "anchor": { "raw": "0200000075e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f3657853c04a1539a855d182522ac98", - "hash": "00000000000000001b44d4645ac00773be676f3de8a8bff1a5fdd1fb04d2b3b2", - "hash_le": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", + "hash": "b2b3d204fbd1fda5f1bfa8e83d6f67be7307c05a64d4441b0000000000000000", "height": 306431, - "prevhash": "000000000000000051273a083f9dd20a4c500f00db9f61ff01b57407675ae975", - "prevhash_le": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", - "merkle_root": "8557365fd36ad19bd78aaca0d8b3a47db8b07b5492e4a88306e22e3c1f3713b0", - "merkle_root_le": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" + "prevhash": "75e95a670774b501ff619fdb000f504c0ad29d3f083a27510000000000000000", + "merkle_root": "b013371f3c2ee20683a8e492547bb0b87da4b3d8a0ac8ad79bd16ad35f365785" }, "internal": false, "isMainnet": true, @@ -548,33 +416,24 @@ "headers": [ { "height": 562623, - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" }, { "height": 562625, - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" } ], "anchor": { "height": 562622, - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" }, "internal": false, @@ -586,33 +445,24 @@ "headers": [ { "height": 562623, - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xF16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xF16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" }, { "height": 562624, - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" } ], "anchor": { "height": 562622, - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" }, "internal": false, @@ -624,33 +474,24 @@ "headers": [ { "height": 562623, - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c", "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4" }, { "height": 562624, - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534", "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758" } ], "anchor": { "height": 562622, - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a", "raw": "0xF00000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325" }, "internal": false, @@ -690,166 +531,118 @@ "isMostRecentCommonAncestor": { "orphan": { "raw": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000c52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c9310973d161e5874510418a9f7e0d0", - "hash": "0x0000000000000000011da79d23e40a8360ed09d7f7e67e85e5fcf14377694df2", - "hash_le": "0xf24d697743f1fce5857ee6f7d709ed60830ae4239da71d010000000000000000", + "hash": "0xf24d697743f1fce5857ee6f7d709ed60830ae4239da71d010000000000000000", "height": 437478, - "prevhash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", - "prevhash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", - "merkle_root": "0x9710937c6624a63be51082c04da9c9278b0350977c41347cff5811e3a4392cc5", - "merkle_root_le": "0xc52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c931097" + "prevhash": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "merkle_root": "0xc52c39a4e31158ff7c34417c9750038b27c9a94dc08210e53ba624667c931097" }, "oldPeriodStart": { "raw": "0x00000020a32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801d2a90b58d25504183d4ee3b4", - "hash": "0x00000000000000000082ed5c599748dbaf59b084445c3f0c107fef194fe9009b", - "hash_le": "0x9b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", + "hash": "0x9b00e94f19ef7f100c3f5c4484b059afdb4897595ced82000000000000000000", "height": 435456, - "prevhash": "0x0000000000000000027fd968d41741f31c73c4a3b304472da0165245278e2ea3", - "prevhash_le": "0xa32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000", - "merkle_root": "0x0138c1cf3f774df6793460fd67c0adff82ccb203ca3d73371ad3c24265a48f94", - "merkle_root_le": "0x948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801" + "prevhash": "0xa32e8e27455216a02d4704b3a3c4731cf34117d468d97f020000000000000000", + "merkle_root": "0x948fa46542c2d31a37733dca03b2cc82ffadc067fd603479f64d773fcfc13801" }, "genesis": { "raw": "0x00000020d0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f200000000000000000005e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594af3fe1d58d2550418e90ec632", - "hash": "0x00000000000000000073a63eb7045711d8b7d27f2a13948cd70a858980acd416", - "hash_le": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", + "hash": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", "height": 437466, - "prevhash": "0x000000000000000000209f03eb2c9056fd40d957f6e683bbda8d66a5c98cebd0", - "prevhash_le": "0xd0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f20000000000000000000", - "merkle_root": "0x4a59a169cfb7bc8839eca7c4bff6a6afae1c2ce7d14856143019e81ef57c405e", - "merkle_root_le": "0x5e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594a" + "prevhash": "0xd0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f20000000000000000000", + "merkle_root": "0x5e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594a" }, "preRetargetChain": [ { "raw": "0x0000002016d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f5b001e58d2550418183b1b41", - "hash": "0x0000000000000000033c5bbb5aa43f276e3701168b7e43b40a41dd960101bc58", - "hash_le": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", + "hash": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", "height": 437467, - "prevhash": "0x00000000000000000073a63eb7045711d8b7d27f2a13948cd70a858980acd416", - "prevhash_le": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", - "merkle_root": "0x0fed153bbbb6658eb1c64ca2180d76e3755a4a8e4078902e74bddadbe6563999", - "merkle_root_le": "0x993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f" + "prevhash": "0x16d4ac8089850ad78c94132a7fd2b7d8115704b73ea673000000000000000000", + "merkle_root": "0x993956e6dbdabd742e9078408e4a5a75e3760d18a24cc6b18e65b6bb3b15ed0f" }, { "raw": "0x0000002058bc010196dd410ab4437e8b1601376e273fa45abb5b3c03000000000000000035092ae7558bab7b92b811bd07bd3a5a3c16a84e37a67e47817e813728f6f5e612011e58d255041805ebb671", - "hash": "0x0000000000000000039c463b6149a95a3ba6e0d39aa90755f56477bbd9711901", - "hash_le": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", + "hash": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", "height": 437468, - "prevhash": "0x0000000000000000033c5bbb5aa43f276e3701168b7e43b40a41dd960101bc58", - "prevhash_le": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", - "merkle_root": "0xe6f5f62837817e81477ea6374ea8163c5a3abd07bd11b8927bab8b55e72a0935", - "merkle_root_le": "0x35092ae7558bab7b92b811bd07bd3a5a3c16a84e37a67e47817e813728f6f5e6" + "prevhash": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", + "merkle_root": "0x35092ae7558bab7b92b811bd07bd3a5a3c16a84e37a67e47817e813728f6f5e6" }, { "raw": "0x00000020011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000ea0ac12b8c46d54d1eac6637b916ae7775aad858dc1c8a43cff50c40bb51a3bc2c081e58d255041859d608ec", - "hash": "0x0000000000000000020daace431ae4ab0509e877c069fb35d98a190af3df1473", - "hash_le": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", + "hash": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", "height": 437469, - "prevhash": "0x0000000000000000039c463b6149a95a3ba6e0d39aa90755f56477bbd9711901", - "prevhash_le": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", - "merkle_root": "0xbca351bb400cf5cf438a1cdc58d8aa7577ae16b93766ac1e4dd5468c2bc10aea", - "merkle_root_le": "0xea0ac12b8c46d54d1eac6637b916ae7775aad858dc1c8a43cff50c40bb51a3bc" + "prevhash": "0x011971d9bb7764f55507a99ad3e0a63b5aa949613b469c030000000000000000", + "merkle_root": "0xea0ac12b8c46d54d1eac6637b916ae7775aad858dc1c8a43cff50c40bb51a3bc" }, { "raw": "0x000000207314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000d3439b160498d4464fcb6eee7ac75e801933067932f30234e3b924e2671e31a4e1091e58d25504188ccd158e", - "hash": "0x000000000000000003f58f0d85e555bc292b518fa012a90c8b5e45d24993fff2", - "hash_le": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "hash": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", "height": 437470, - "prevhash": "0x0000000000000000020daace431ae4ab0509e877c069fb35d98a190af3df1473", - "prevhash_le": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", - "merkle_root": "0xa4311e67e224b9e33402f33279063319805ec77aee6ecb4f46d49804169b43d3", - "merkle_root_le": "0xd3439b160498d4464fcb6eee7ac75e801933067932f30234e3b924e2671e31a4" + "prevhash": "0x7314dff30a198ad935fb69c077e80905abe41a43ceaa0d020000000000000000", + "merkle_root": "0xd3439b160498d4464fcb6eee7ac75e801933067932f30234e3b924e2671e31a4" }, { "raw": "0x00000020f2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff503000000000000000051aa623be08b19c2a6984e0c229bbffce20673675267eab5bf09a963b0547c673c0c1e58d25504183e20ab5b", - "hash": "0x00000000000000000364a23184b8a2c009d13172094421c22e4d9bc85dcf90a5", - "hash_le": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", + "hash": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", "height": 437471, - "prevhash": "0x000000000000000003f58f0d85e555bc292b518fa012a90c8b5e45d24993fff2", - "prevhash_le": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", - "merkle_root": "0x677c54b063a909bfb5ea6752677306e2fcbf9b220c4e98a6c2198be03b62aa51", - "merkle_root_le": "0x51aa623be08b19c2a6984e0c229bbffce20673675267eab5bf09a963b0547c67" + "prevhash": "0xf2ff9349d2455e8b0ca912a08f512b29bc55e5850d8ff5030000000000000000", + "merkle_root": "0x51aa623be08b19c2a6984e0c229bbffce20673675267eab5bf09a963b0547c67" } ], "postRetargetChain": [ { "raw": "0x00000020a590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000a37e2d39aaf31d8bd7c00a07fa9784fee88ab361082f03601119dac4a0d87c8d020d1e58745104189b665b65", - "hash": "0x000000000000000003c75c23f1444676c6832d45badd6ae89b8d1903d92563b7", - "hash_le": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "hash": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", "height": 437472, - "prevhash": "0x00000000000000000364a23184b8a2c009d13172094421c22e4d9bc85dcf90a5", - "prevhash_le": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", - "merkle_root": "0x8d7cd8a0c4da191160032f0861b38ae8fe8497fa070ac0d78b1df3aa392d7ea3", - "merkle_root_le": "0xa37e2d39aaf31d8bd7c00a07fa9784fee88ab361082f03601119dac4a0d87c8d" + "prevhash": "0xa590cf5dc89b4d2ec22144097231d109c0a2b88431a264030000000000000000", + "merkle_root": "0xa37e2d39aaf31d8bd7c00a07fa9784fee88ab361082f03601119dac4a0d87c8d" }, { "raw": "0x00000020b76325d903198d9be86addba452d83c6764644f1235cc70300000000000000001d9e6cc2b0871a8f88db857db870dfa7000f80f3c89c7a0eaaa925974fc392459e111e5874510418f4dcbaed", - "hash": "0x0000000000000000006084d9f6fb8b81349fcde72e85ab5e39e9d0b728f32b61", - "hash_le": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", + "hash": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", "height": 437473, - "prevhash": "0x000000000000000003c75c23f1444676c6832d45badd6ae89b8d1903d92563b7", - "prevhash_le": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", - "merkle_root": "0x4592c34f9725a9aa0e7a9cc8f3800f00a7df70b87d85db888f1a87b0c26c9e1d", - "merkle_root_le": "0x1d9e6cc2b0871a8f88db857db870dfa7000f80f3c89c7a0eaaa925974fc39245" + "prevhash": "0xb76325d903198d9be86addba452d83c6764644f1235cc7030000000000000000", + "merkle_root": "0x1d9e6cc2b0871a8f88db857db870dfa7000f80f3c89c7a0eaaa925974fc39245" }, { "raw": "0x00000020612bf328b7d0e9395eab852ee7cd9f34818bfbf6d984600000000000000000001698022f9aeaccae63250f24ad66093504b088686d2e307bf7653c700b7e23d1e2111e5874510418914866a7", - "hash": "0x000000000000000003a15741d88dcbdab6c39b17dddf4829b6b8b509313bb14d", - "hash_le": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "hash": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", "height": 437474, - "prevhash": "0x0000000000000000006084d9f6fb8b81349fcde72e85ab5e39e9d0b728f32b61", - "prevhash_le": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", - "merkle_root": "0xd1237e0b703c65f77b302e6d6888b004350966ad240f2563aeccea9a2f029816", - "merkle_root_le": "0x1698022f9aeaccae63250f24ad66093504b088686d2e307bf7653c700b7e23d1" + "prevhash": "0x612bf328b7d0e9395eab852ee7cd9f34818bfbf6d98460000000000000000000", + "merkle_root": "0x1698022f9aeaccae63250f24ad66093504b088686d2e307bf7653c700b7e23d1" }, { "raw": "0x000000204db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a10300000000000000005c93b3ce08ce22ba64fb26b8e75d695ac8c2bea0badd34200e698ccc73ca421b97121e58745104183ded0054", - "hash": "0x000000000000000000a024f601c50d9da8c3f09cc8feba477dd11370756fcda8", - "hash_le": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "hash": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", "height": 437475, - "prevhash": "0x000000000000000003a15741d88dcbdab6c39b17dddf4829b6b8b509313bb14d", - "prevhash_le": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", - "merkle_root": "0x1b42ca73cc8c690e2034ddbaa0bec2c85a695de7b826fb64ba22ce08ceb3935c", - "merkle_root_le": "0x5c93b3ce08ce22ba64fb26b8e75d695ac8c2bea0badd34200e698ccc73ca421b" + "prevhash": "0x4db13b3109b5b8b62948dfdd179bc3b6dacb8dd84157a1030000000000000000", + "merkle_root": "0x5c93b3ce08ce22ba64fb26b8e75d695ac8c2bea0badd34200e698ccc73ca421b" }, { "raw": "0x00000020a8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a00000000000000000004d7fd5d8c9984f2be03a80a75a4b6fe02dfda894181fcab0b6beb60698c7d6f0db121e587451041816e7fbbd", - "hash": "0x000000000000000003464978084011690021563526c9a5e2cbb17fbb76073adb", - "hash_le": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", + "hash": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", "height": 437476, - "prevhash": "0x000000000000000000a024f601c50d9da8c3f09cc8feba477dd11370756fcda8", - "prevhash_le": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", - "merkle_root": "0xf0d6c79806b6beb6b0ca1f1894a8fd2de06f4b5aa7803ae02b4f98c9d8d57f4d", - "merkle_root_le": "0x4d7fd5d8c9984f2be03a80a75a4b6fe02dfda894181fcab0b6beb60698c7d6f0" + "prevhash": "0xa8cd6f757013d17d47bafec89cf0c3a89d0dc501f624a0000000000000000000", + "merkle_root": "0x4d7fd5d8c9984f2be03a80a75a4b6fe02dfda894181fcab0b6beb60698c7d6f0" }, { "raw": "0x00000020db3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000c8cbccf35d668296e8da72aed974983a8679e5077445b79d1e3e6c4d1bbff2c730161e58745104188674ac6f", - "hash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", - "hash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "hash": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", "height": 437477, - "prevhash": "0x000000000000000003464978084011690021563526c9a5e2cbb17fbb76073adb", - "prevhash_le": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", - "merkle_root": "0xc7f2bf1b4d6c3e1e9db7457407e579863a9874d9ae72dae89682665df3cccbc8", - "merkle_root_le": "0xc8cbccf35d668296e8da72aed974983a8679e5077445b79d1e3e6c4d1bbff2c7" + "prevhash": "0xdb3a0776bb7fb1cbe2a5c9263556210069114008784946030000000000000000", + "merkle_root": "0xc8cbccf35d668296e8da72aed974983a8679e5077445b79d1e3e6c4d1bbff2c7" }, { "raw": "0x00000020e156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000f7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c073347161e58745104186cff9846", - "hash": "0x0000000000000000013f1f231297f03c154bf918650793a786b3468ab9c339d3", - "hash_le": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", + "hash": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", "height": 437478, - "prevhash": "0x000000000000000000fb1362142f297157b6909f44bdb7e7db38c76d20ef56e1", - "prevhash_le": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", - "merkle_root": "0x33074cebe67c59e730333ae386f8bd8f05fdda32c08fe143ac2b44e5e22ae2f7", - "merkle_root_le": "0xf7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c0733" + "prevhash": "0xe156ef206dc738dbe7b7bd449f90b65771292f146213fb000000000000000000", + "merkle_root": "0xf7e22ae2e5442bac43e18fc032dafd058fbdf886e33a3330e7597ce6eb4c0733" }, { "raw": "0x00000020d339c3b98a46b386a793076518f94b153cf09712231f3f01000000000000000018f078c9c73734d892fef90854381524f30017370d12dfa8319313c5729dbf85b8191e58745104187d43c005", - "hash": "0x000000000000000003a243f554646535630675ac1fb5a922a5bacc4df24d77a0", - "hash_le": "0xa0774df24dccbaa522a9b51fac75066335656454f543a2030000000000000000", + "hash": "0xa0774df24dccbaa522a9b51fac75066335656454f543a2030000000000000000", "height": 437479, - "prevhash": "0x0000000000000000013f1f231297f03c154bf918650793a786b3468ab9c339d3", - "prevhash_le": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", - "merkle_root": "0x85bf9d72c5139331a8df120d371700f32415385408f9fe92d83437c7c978f018", - "merkle_root_le": "0x18f078c9c73734d892fef90854381524f30017370d12dfa8319313c5729dbf85" + "prevhash": "0xd339c3b98a46b386a793076518f94b153cf09712231f3f010000000000000000", + "merkle_root": "0x18f078c9c73734d892fef90854381524f30017370d12dfa8319313c5729dbf85" } ], "testCases": [{ @@ -887,214 +680,151 @@ "heaviestFromAncestor": { "orphan": { "raw": "0x0000802020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f0500000000000000000058c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53e7d3615c886f2e17ed205930", - "hash": "0x0000000000000000000f021d6ba1384b7db92589b2eee80d01dcc0d5afb30452", - "hash_le": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000", + "hash": "0x5204b3afd5c0dc010de8eeb28925b97d4b38a16b1d020f000000000000000000", "height": 562630, - "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", - "merkle_root": "0x532bc27f15535eb6e606e34d16dedceb333513b212f56f3d0ce0e8f86bafc858", - "merkle_root_le": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" + "prevhash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" }, "badHeader": { "raw": "0x00000020fe70e48339d6b17fbbf1340d245338f57336e97767cc240000000000000000005af53b865c27c6e9b5e5db4c3ea8e024f8329178a79ddb39f7727ea2fe6e6825d1349c5ba1192817e2d95159", - "hash": "0x0000000000000000000f021d6ba1384b7db92589b2eee80d01dcc0d5afb30452", - "hash_le": "0xbaaea6746f4c16ccb7cd961655b636d39b5fe1519b8f15000000000000000000", + "hash": "0xbaaea6746f4c16ccb7cd961655b636d39b5fe1519b8f15000000000000000000", "height": 562630, - "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", - "merkle_root": "0x532bc27f15535eb6e606e34d16dedceb333513b212f56f3d0ce0e8f86bafc858", - "merkle_root_le": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" + "prevhash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x58c8af6bf8e8e00c3d6ff512b2133533ebdcde164de306e6b65e53157fc22b53" }, "genesis": { "raw": "0x00000020db62962b5989325f30f357762ae456b2ec340432278e14000000000000000000d1dd4e30908c361dfeabfb1e560281c1a270bde3c8719dbda7c848005317594440bf615c886f2e17bd6b082d", - "hash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "hash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "hash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", "height": 562621, - "prevhash": "0x000000000000000000148e27320434ecb256e42a7657f3305f3289592b9662db", - "prevhash_le": "0xdb62962b5989325f30f357762ae456b2ec340432278e14000000000000000000", - "merkle_root": "0x445917530048c8a7bd9d71c8e3bd70a2c18102561efbabfe1d368c90304eddd1", - "merkle_root_le": "0xd1dd4e30908c361dfeabfb1e560281c1a270bde3c8719dbda7c8480053175944" + "prevhash": "0xdb62962b5989325f30f357762ae456b2ec340432278e14000000000000000000", + "merkle_root": "0xd1dd4e30908c361dfeabfb1e560281c1a270bde3c8719dbda7c8480053175944" }, "headers": [ { "raw": "0x000000204615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000b034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a34c0615c886f2e17046e7325", - "hash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "hash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "hash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", "height": 562622, - "prevhash": "0x00000000000000000021ce9ccc1691e25066eb388be7821a4906dbee4b611546", - "prevhash_le": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", - "merkle_root": "0x7a28cceb1fa00385a5759d45aa0d615a7fd8e07bf61a86cc1aff85c24f8834b0", - "merkle_root_le": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a" + "prevhash": "0x4615614beedb06491a82e78b38eb6650e29116cc9cce21000000000000000000", + "merkle_root": "0xb034884fc285ff1acc861af67be0d87f5a610daa459d75a58503a01febcc287a" }, { "raw": "0x00000020b8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000b16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c44c0615c886f2e1744ea7cc4", - "hash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "hash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "hash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", "height": 562623, - "prevhash": "0x0000000000000000000f498a3b8394685a70ba0b0d8cb27850b1f499a380b5b8", - "prevhash_le": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", - "merkle_root": "0x4c9d8555398eb7df62cb0c399acc30153233e7b9bbfee74907b7d336aa326cb1", - "merkle_root_le": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c" + "prevhash": "0xb8b580a399f4b15078b28c0d0bba705a6894833b8a490f000000000000000000", + "merkle_root": "0xb16c32aa36d3b70749e7febbb9e733321530cc9a390ccb62dfb78e3955859d4c" }, { "raw": "0x00000020f549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000af9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf25753430c2615c886f2e170a654758", - "hash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "hash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "hash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", "height": 562624, - "prevhash": "0x0000000000000000002066539e823973bbd8174973a5af1634ab56f685a949f5", - "prevhash_le": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", - "merkle_root": "0x347525bf8f9417aa7a383898cb84ad79f091efdcc8b582f39cc39424e29f9caf", - "merkle_root_le": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534" + "prevhash": "0xf549a985f656ab3416afa5734917d8bb7339829e536620000000000000000000", + "merkle_root": "0xaf9c9fe22494c39cf382b5c8dcef91f079ad84cb9838387aaa17948fbf257534" }, { "raw": "0x00e0ff3f8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000e78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca8149dc3615c886f2e171c3fa2b8", - "hash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", - "hash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "hash": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "height": 562625, - "prevhash": "0x00000000000000000014c9dc88648827978218b292dbdf0aa92aaf28aef9ff8b", - "prevhash_le": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", - "merkle_root": "0x14a87c0e6d701c62e4cb4f3034f55bcbfd670657aa853e9b46ef9524c16582e7", - "merkle_root_le": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814" + "prevhash": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", + "merkle_root": "0xe78265c12495ef469b3e85aa570667fdcb5bf534304fcbe4621c706d0e7ca814" }, { "raw": "0x00000020fee20039fe494f7408c090e03970ec9b132366066f380d0000000000000000006f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533dacec3615c886f2e170a1a921d", - "hash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", - "hash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "hash": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "height": 562626, - "prevhash": "0x0000000000000000000d386f066623139bec7039e090c008744f49fe3900e2fe", - "prevhash_le": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", - "merkle_root": "0xda33c59bc6f3c950d8bc9be1779b0c03ddc67f7ab9304fb62cd456a1840b516f", - "merkle_root_le": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da" + "prevhash": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", + "merkle_root": "0x6f510b84a156d42cb64f30b97a7fc6dd030c9b77e19bbcd850c9f3c69bc533da" }, { "raw": "0x00000020d2406bb15e4f917104d2b2d9320454b947fb08a723301d0000000000000000006aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20d1c5615c886f2e1767973d73", - "hash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", - "hash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "hash": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", "height": 562627, - "prevhash": "0x0000000000000000001d3023a708fb47b9540432d9b2d20471914f5eb16b40d2", - "prevhash_le": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", - "merkle_root": "0x20ead5553be95c71e87839492197ea48ccf87dbeb0b25ed4bc4e97bb0214a36a", - "merkle_root_le": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20" + "prevhash": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", + "merkle_root": "0x6aa31402bb974ebcd45eb2b0be7df8cc48ea9721493978e8715ce93b55d5ea20" }, { "raw": "0x00e0002096f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b0000000000000000002488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe953ce615c886f2e172e0885ce", - "hash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", - "hash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "hash": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", "height": 562628, - "prevhash": "0x0000000000000000002b2d22288b3c99e828b238bbd680e2f41b0ad3345df296", - "prevhash_le": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", - "merkle_root": "0xe9ab8cb51b64c924b61c52f8be389955c983ae2315ee042dc76cab3b3a338824", - "merkle_root_le": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9" + "prevhash": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", + "merkle_root": "0x2488333a3bab6cc72d04ee1523ae83c9559938bef8521cb624c9641bb58cabe9" }, { "raw": "0x00000020b86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000e72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7fcdce615c886f2e17415cdb46", - "hash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "hash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "hash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", "height": 562629, - "prevhash": "0x0000000000000000002470635e016bac1e96ed4a68af6a3b9b606062701869b8", - "prevhash_le": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", - "merkle_root": "0x7f8d7afb20451eb0dfd026ecc2f71e9f126fc536939fe3de6ed324d36f0a2ee7", - "merkle_root_le": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f" + "prevhash": "0xb86918706260609b3b6aaf684aed961eac6b015e637024000000000000000000", + "merkle_root": "0xe72e0a6fd324d36edee39f9336c56f129f1ef7c2ec26d0dfb01e4520fb7a8d7f" }, { "raw": "0x0000002020ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f050000000000000000009d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ecebd3615c886f2e179234c38e", - "hash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", - "hash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "hash": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", "height": 562630, - "prevhash": "0x000000000000000000055f9b2b220c70f52fa5e7f7ad1036a8fa05a0f7e4ff20", - "prevhash_le": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", - "merkle_root": "0xec8294c76e141d9244c6fec847fea8dbbb529933b279a2102a61da7f5179149d", - "merkle_root_le": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec" + "prevhash": "0x20ffe4f7a005faa83610adf7e7a52ff5700c222b9b5f05000000000000000000", + "merkle_root": "0x9d1479517fda612a10a279b2339952bbdba8fe47c8fec644921d146ec79482ec" }, { "raw": "0x0000002051214b0c42383a1ea7bf28f20062f81d7b72497cb1030a00000000000000000000af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab3314bd8615c886f2e17f152bc1f", - "hash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", - "hash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "hash": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", "height": 562631, - "prevhash": "0x0000000000000000000a03b17c49727b1df86200f228bfa71e3a38420c4b2151", - "prevhash_le": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", - "merkle_root": "0x31b36a8983956a484b5ba867fa9cd7e70ae0b4c78dcbe6da1353eb564744af00", - "merkle_root_le": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331" + "prevhash": "0x51214b0c42383a1ea7bf28f20062f81d7b72497cb1030a000000000000000000", + "merkle_root": "0x00af444756eb5313dae6cb8dc7b4e00ae7d79cfa67a85b4b486a9583896ab331" }, { "raw": "0x00e00020bf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000b2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d032db615c886f2e17155f22df", - "hash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", - "hash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "hash": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", "height": 562632, - "prevhash": "0x0000000000000000000864d592bfb200c2933037055280b971f9f4e15c5101bf", - "prevhash_le": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", - "merkle_root": "0xd029d1fadb3f9e673ff5155d227918c860962dcc1999bb77d6e2d655b5fcc2b2", - "merkle_root_le": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0" + "prevhash": "0xbf01515ce1f4f971b9805205373093c200b2bf92d56408000000000000000000", + "merkle_root": "0xb2c2fcb555d6e2d677bb9919cc2d9660c81879225d15f53f679e3fdbfad129d0" }, { "raw": "0x00000020a4c292016c1585e6f81986f7e216c79d28b15b1d513a100000000000000000004e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35c0de615c886f2e17032ddf4b", - "hash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", - "hash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "hash": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", "height": 562633, - "prevhash": "0x000000000000000000103a511d5bb1289dc716e2f78619f8e685156c0192c2a4", - "prevhash_le": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", - "merkle_root": "0x351f74fe8fab94e161ce4f9d2f2da2cac1b7af93abe7054a1d37635f5cf7334e", - "merkle_root_le": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35" + "prevhash": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", + "merkle_root": "0x4e33f75c5f63371d4a05e7ab93afb7c1caa22d2f9d4fce61e194ab8ffe741f35" }, { "raw": "0x00000020577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000ef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180cdde615c886f2e171f2b5f80", - "hash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", - "hash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "hash": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", "height": 562634, - "prevhash": "0x00000000000000000020cc2730fa1682e8db65dd5a3b34483773905fb4117d57", - "prevhash_le": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", - "merkle_root": "0x80214eef007bb437affecc7279cd81fa215e40096f47622d90e19ab0520d20ef", - "merkle_root_le": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180" + "prevhash": "0x577d11b45f90733748343b5add65dbe88216fa3027cc20000000000000000000", + "merkle_root": "0xef200d52b09ae1902d62476f09405e21fa81cd7972ccfeaf37b47b00ef4e2180" }, { "raw": "0x00000020f5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000a91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589cb3e0615c886f2e17c624e58c", - "hash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", - "hash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "hash": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", "height": 562635, - "prevhash": "0x00000000000000000025b7bece641ab4df35f145f488bd8192ff120184d6f2f5", - "prevhash_le": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", - "merkle_root": "0x9c58834eb6b069f57886efe06fe2af4be50cf82446160609e728867bfc3817a9", - "merkle_root_le": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c" + "prevhash": "0xf5f2d6840112ff9281bd88f445f135dfb41a64cebeb725000000000000000000", + "merkle_root": "0xa91738fc7b8628e70906164624f80ce54bafe26fe0ef8678f569b0b64e83589c" }, { "raw": "0x00e00020f98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000c29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2fae4615c886f2e178a505d9d", - "hash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", - "hash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "hash": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", "height": 562636, - "prevhash": "0x00000000000000000000c0be9a85e087242e1bb31aa3b27ef0251eb7c59487f9", - "prevhash_le": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", - "merkle_root": "0xc2c7bcd4be5c099df57632870ccf7cc345df60d46576197321ac90fef0149bc2", - "merkle_root_le": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2" + "prevhash": "0xf98794c5b71e25f07eb2a31ab31b2e2487e0859abec000000000000000000000", + "merkle_root": "0xc29b14f0fe90ac2173197665d460df45c37ccf0c873276f59d095cbed4bcc7c2" }, { "raw": "0x000000200ed18ffcb751e45471dddab23d34538869d3b2cdd484280000000000000000003505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d657ce5615c886f2e17d759c936", - "hash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", - "hash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "hash": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", "height": 562637, - "prevhash": "0x0000000000000000002884d4cdb2d3698853343db2dadd7154e451b7fc8fd10e", - "prevhash_le": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", - "merkle_root": "0x650d7ffc89ce1160ed30f83a2c0bbe807f289987076e19f1916f861929700535", - "merkle_root_le": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65" + "prevhash": "0x0ed18ffcb751e45471dddab23d34538869d3b2cdd48428000000000000000000", + "merkle_root": "0x3505702919866f91f1196e078799287f80be0b2c3af830ed6011ce89fc7f0d65" }, { "raw": "0x0000802030ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882f1e5615c886f2e1796e65983", - "hash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", - "hash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "hash": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", "height": 562638, - "prevhash": "0x0000000000000000002a0336c2ede62484bcab6b1f345c42b0002ad6c817ae30", - "prevhash_le": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", - "merkle_root": "0x8258a466e5628d96ced147e5e18955923699b2daad38be299e42490880327a09", - "merkle_root_le": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882" + "prevhash": "0x30ae17c8d62a00b0425c341f6babbc8424e6edc236032a000000000000000000", + "merkle_root": "0x097a32800849429e29be38addab29936925589e1e547d1ce968d62e566a45882" }, { "raw": "0x0000802051f32093b60f4484c041d383fb6bf35c44d428bde54d2d0000000000000000001888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee905e6615c886f2e177b59d28f", - "hash": "0x0000000000000000000fb8dba7f48eb65dbc24c6dc03d6f84823b5d59c7c4148", - "hash_le": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", + "hash": "0x48417c9cd5b52348f8d603dcc624bc5db68ef4a7dbb80f000000000000000000", "height": 562639, - "prevhash": "0x0000000000000000002d4de5bd28d4445cf36bfb83d341c084440fb69320f351", - "prevhash_le": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", - "merkle_root": "0xe9bedf6cd3fe3df1a7ba1d194d42a5caca6a3b323ed28270c2db2ff72d698818", - "merkle_root_le": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9" + "prevhash": "0x51f32093b60f4484c041d383fb6bf35c44d428bde54d2d000000000000000000", + "merkle_root": "0x1888692df72fdbc27082d23e323b6acacaa5424d191dbaa7f13dfed36cdfbee9" } ], "testCases": [{ @@ -1203,18 +933,14 @@ "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "locktime": "0x00000000", - "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", - "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "tx_id": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", "index": 26, "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d","confirming_header": { - "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", - "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "hash": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", "height": 592920, "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", - "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", - "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", - "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", - "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + "merkle_root": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" } }, "bestKnown": { @@ -1230,18 +956,14 @@ "vin": "0x0102748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "locktime": "0x00000000", - "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", - "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "tx_id": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", "index": 26, "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d","confirming_header": { - "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", - "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "hash": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", "height": 592920, "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", - "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", - "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", - "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", - "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + "merkle_root": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" } }, "lca": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000", @@ -1254,19 +976,15 @@ "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "locktime": "0x00000000", - "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", - "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "tx_id": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", "index": 26, "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", "confirming_header": { - "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", - "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "hash": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", "height": 592920, "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", - "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", - "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", - "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", - "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + "merkle_root": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" } }, "requests": [{ @@ -1283,19 +1001,15 @@ "vin": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "locktime": "0x00000000", - "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", - "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "tx_id": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", "index": 26, "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", "confirming_header": { - "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", - "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "hash": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", "height": 592920, "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", - "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", - "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", - "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", - "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + "merkle_root": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" } }, "requests": [{ @@ -1312,19 +1026,15 @@ "vin": "0x0101748906a5c7064550a594c4683ffc6d1ee25292b638c4328bb66403cfceb58a000000006a4730440220364301a77ee7ae34fa71768941a2aad5bd1fa8d3e30d4ce6424d8752e83f2c1b02203c9f8aafced701f59ffb7c151ff2523f3ed1586d29b674efb489e803e9bf93050121029b3008c0fa147fd9db5146e42b27eb0a77389497713d3aad083313d1b1b05ec0ffffffff", "vout": "0x0316312f00000000001976a91400cc8d95d6835252e0d95eb03b11691a21a7bac588ac220200000000000017a914e5034b9de4881d62480a2df81032ef0299dcdc32870000000000000000166a146f6d6e69000000000000001f0000000315e17900", "locktime": "0x00000000", - "tx_id": "0x74d6d6dc1fc9b0f393abde12e76adeeb3d674b38b7fbea4d9fc28b3bb0f67651", - "tx_id_le": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", + "tx_id": "0x5176f6b03b8bc29f4deafbb7384b673debde6ae712deab93f3b0c91fdcd6d674", "index": 26, "intermediate_nodes": "0x8d7a6d53ce27f79802631f1aae5f172c43d128b210ab4962d488c81c96136cfb75c95def872e878839bd93b42c04eb44da44c401a2d580ca343c3262e9c0a2819ed4bbfb9ea620280b31433f43b2512a893873b8c8c679f61e1a926c0ec80bcfc6225a15d72fbd1116f78b14663d8518236b02e765bf0a746a6a08840c122a02afa4df3ab6b9197a20f00495a404ee8e07da2b7554e94609e9ee1d5da0fb7857ea0332072568d0d53a9aedf851892580504a7fcabfbdde076242eb7f4e5f218a14d2a3f357d950b4f6a1dcf93f7c19c44d0fc122d00afa297b9503c1a6ad24cf36cb5f2835bcf490371db2e96047813a24176c3d3416f84b7ddfb7d8c915eb0c5ce7de089b5d9e700ecd12e09163f173b70bb4c9af33051b466b1f55abd66f3121216ad0ad9dfa898535e1d5e51dd07bd0a73d584daace7902f20ece4ba4f4f241c80cb31eda88a244a3c68d0f157c1049b4153d7addd6548aca0885acafbf98a1f8345c89914c24729ad095c7a0b9acd20232ccd90dbd359468fcc4eee7b67d", "confirming_header": { - "hash": "0x00000000000000000016633b88de22bd6462283bcf7dcbe559233baaf5fb0c4d", - "hash_le": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", + "hash": "0x4d0cfbf5aa3b2359e5cb7dcf3b286264bd22de883b6316000000000000000000", "height": 592920, "raw": "0x0000c020c238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000b61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2ddd6376d5d3e211a17d8706a84", - "merkle_root": "0xdde25e5d1cb29ac6c08be7378373c646ad18fc90b14436a92ac8ab4228c91ab6", - "merkle_root_le": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", - "prevhash": "0x00000000000000000001103ad2a8ce7e7d2d9459edb26a3497728b3001b638c2", - "prevhash_le": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" + "merkle_root": "0xb61ac92842abc82aa93644b190fc18ad46c6738337e78bc0c69ab21c5d5ee2dd", + "prevhash": "0xc238b601308b7297346ab2ed59942d7d7ecea8d23a1001000000000000000000" } }, "requests": [{ From 96a84ae1059cb19e213ed13047465fd72ec07b3f Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Mon, 13 Apr 2020 16:34:24 -0400 Subject: [PATCH 093/148] Integration tests (#57) * Integration Tests * Progress on Integration Tests * Stilllll goinnn * Everything has a test now * Order tests in same order as commands are listed in CLI * clean clean cleaning * Cl3an * woops * TrimSuffix + faster tests * use suite module * Add Integration Test Instructions * Add invalid conditions * linting * README update * Correct defer pattern * Add build directory search attempt * linting * SLASH * take out extra build options for integration tests * update btcspv in go * nothing much * removed le from structs * rebasing Co-authored-by: Erin Hales --- golang/cli_test/README.md | 56 +++ golang/cli_test/cli_test.go | 457 ++++++++++++++++++ golang/cli_test/test_helpers.go | 451 +++++++++++++++++ golang/cmd/relaycli/main.go | 2 +- .../scripts/json_data/0_new_difficulty.json | 18 +- golang/scripts/json_data/1_check_proof.json | 12 +- .../scripts/json_data/2_ingest_headers.json | 99 ++-- .../scripts/json_data/4_ingest_headers.json | 54 +-- golang/scripts/json_data/genesis.json | 18 +- golang/x/relay/client/cli/utils.go | 11 +- golang/x/relay/gen_state.go | 14 +- golang/x/relay/types/querier.go | 1 - 12 files changed, 1054 insertions(+), 139 deletions(-) create mode 100644 golang/cli_test/README.md create mode 100644 golang/cli_test/cli_test.go create mode 100644 golang/cli_test/test_helpers.go diff --git a/golang/cli_test/README.md b/golang/cli_test/README.md new file mode 100644 index 00000000..57c11753 --- /dev/null +++ b/golang/cli_test/README.md @@ -0,0 +1,56 @@ +# Relay CLI Integration tests + +The relay cli integration tests live in this folder. You can run the full suite by running: + +```bash +go test -mod=readonly -p 4 `go list ./cli_test/...` +``` + + To run a single test run: + ```bash + go test -mod=readonly -p 4 `go list ./cli_test/...` -testify.m TestName + ``` + +> NOTE: While the full suite runs in parallel, some of the tests can take up to a minute to complete + +### Test Structure + +This integration suite [uses a thin wrapper](https://godoc.org/github.com/cosmos/cosmos-sdk/tests) over the [`os/exec`](https://golang.org/pkg/os/exec/) package. This allows the integration test to run against built binaries (both `relayd` and `relaycli` are used) while being written in golang. This allows tests to take advantage of the various golang code we have for operations like marshal/unmarshal, crypto, etc... + +> NOTE: The tests will use whatever `relayd` or `relaycli` binaries are available in your `$GOPATH/bin`. You can check which binary will be run by the suite by running `which relayd` or `which relaycli`. If you have your `$GOPATH` properly setup they should be in `$GOPATH/bin/relay*`. This will ensure that your test uses the latest binary you have built + +Tests generally follow this structure: + +```go +func (suite *UtilsSuite) TestMyNewCommand() { + suite.T().Parallel() + f := InitFixtures(suite.T()) + + // start relayd server + proc := f.GDStart() + defer proc.Stop(false) + + // Your test code goes here... + + f.Cleanup() +} +``` + +This boilerplate above: + +- Ensures the tests run in parallel. Because the tests are calling out to `os/exec` for many operations these tests can take a long time to run. +- Creates `.relayd` and `.relaycli` folders in a new temp folder. +- Uses `relaycli` to create test account for use in testing: `foo` +- Creates a genesis file with coins (`1000footoken,1000feetoken,150stake`) controlled by the `foo` key +- Generates an initial bonding transaction (`gentx`) to make the `foo` key a validator at genesis +- Starts `relayd` and stops it once the test exits +- Cleans up test state on a successful run + +### Notes when adding/running tests + +- Because the tests run against a built binary, you should make sure you build every time the code changes and you want to test again, otherwise you will be testing against an older version. If you are adding new tests this can easily lead to confusing test results. +- The [`test_helpers.go`](./test_helpers.go) file is organized according to the format of `relaycli` and `relayd` commands. There are comments with section headers describing the different areas. Helper functions to call CLI functionality are generally named after the command (e.g. `relaycli query bestknowndigest` would be `QueryBestKnownDigest`). Try to keep functions grouped by their position in the command tree. +- Test state that is needed by `tx` and `query` commands (`home`, `chain_id`, etc...) is stored on the `Fixtures` object. This makes constructing your new tests almost trivial. Each test needs unique Fixture to run in parallel +- Sometimes if you exit a test early there can be still running `relayd` and `relaycli` processes that will interrupt subsequent runs. Still running `relayd` processes will block ports and prevent new tests from spinning up. You can ensure new tests spin up clean by running `pkill -9 relayd && pkill -9 relaycli` before each test run. +- Most `query` and `tx` commands take a variadic `flags` argument. This pattern allows for the creation of a general function which is easily modified by adding flags. +- `Tx*` functions follow a general pattern and return `(success bool, stdout string, stderr string)`. This allows for easy testing of multiple different flag configurations. diff --git a/golang/cli_test/cli_test.go b/golang/cli_test/cli_test.go new file mode 100644 index 00000000..aaad1118 --- /dev/null +++ b/golang/cli_test/cli_test.go @@ -0,0 +1,457 @@ +package clitest + +import ( + "encoding/hex" + "github.com/stretchr/testify/suite" + "testing" +) + +type UtilsSuite struct { + suite.Suite + TestData TestData +} + +// Runs the whole test suite +func TestRelay(t *testing.T) { + + utilsSuite := new(UtilsSuite) + utilsSuite.TestData = GrabTestData(t) + + suite.Run(t, utilsSuite) +} + +func (suite *UtilsSuite) TestRelayCLIIsAncestor() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + newDiffHeaders := suite.TestData.NewDiffHeaders + + // Initialize CHain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // define param values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + validAncestor := hex.EncodeToString(genesisHeaders[1].Hash[:]) + invalidAncestor := hex.EncodeToString(genesisHeaders[0].Hash[:]) + digest := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + limit := "5" + + // must ingest headers in order to perform query + success, _, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + + // query chain for actual ancestor value + isancestor := f.QueryIsAncestor(digest, validAncestor, limit) + actual := isancestor.Res + + // True Condition + expected := true + suite.Equal(expected, actual) + + // False Condition + expected = false + isancestor = f.QueryIsAncestor(digest, invalidAncestor, limit) + actual = isancestor.Res + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLIGetRelayGenesis() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Query Chain for Actual Value + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + fooAddr := f.KeyAddress(keyFoo) + genesisRelay := f.QueryGetRelayGenesis(fooAddr) + actual := genesisRelay.Res + + // Condition + expected := genesisHeaders[1].Hash + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLIGetLastReorgLCA() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Query Chain for Actual Value + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + fooAddr := f.KeyAddress(keyFoo) + lastReorgLCA := f.QueryGetLastReorgLCA(fooAddr) + actual := lastReorgLCA.Res + + // Condition + expected := genesisHeaders[1].Hash + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLIGetBestDigest() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Query Chain for Actual Value + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + fooAddr := f.KeyAddress(keyFoo) + bestDigest := f.QueryGetBestDigest(fooAddr) + actual := bestDigest.Res + + // Condition + expected := genesisHeaders[1].Hash + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLIQueryFindAncestor() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + newDiffHeaders := suite.TestData.NewDiffHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // define paramater values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + digest := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + invalidOffset := "5" + validOffset := "1" + + // ingest headers + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Require findancestor fails if ancestor does not exist on relay + f.QueryFindAncestorInvalid("could not find ancestor", digest, invalidOffset) + + // Require findancestor returns ancestor if valid query + findancestor := f.QueryFindAncestor(digest, validOffset) + actual := hex.EncodeToString(findancestor.Res[:]) + expected := hex.EncodeToString(newDiffHeaders[0].Hash[:]) + suite.Equal(expected, actual) +} + +func (suite *UtilsSuite) TestRelayCLIIsMostRecentCommonAncestor() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + newDiffHeaders := suite.TestData.NewDiffHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // must ingest headers in order to perform query + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + success, _, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + + //perform query + ancestor := hex.EncodeToString(newDiffHeaders[0].Hash[:]) + left := hex.EncodeToString(newDiffHeaders[0].Hash[:]) + right := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + limit := "3" + commonancestor := f.QueryIsMostRecentCommonAncestor(ancestor, left, right, limit) + actual := commonancestor.Res + + // True Condition + expected := true + suite.Equal(expected, actual) + + // False Condition + invalidAncestor := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + commonancestor = f.QueryIsMostRecentCommonAncestor(invalidAncestor, left, right, limit) + expected = false + actual = commonancestor.Res + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLIQueryHeaviestFromAncestor() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + newDiffHeaders := suite.TestData.NewDiffHeaders + + // Transact with Chain for Actual Value + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // define paramteer values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + ancestor := hex.EncodeToString(genesisHeaders[1].Hash[:]) + currentBest := hex.EncodeToString(genesisHeaders[1].Hash[:]) + validNewBest := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + invalidNewBest := hex.EncodeToString(genesisHeaders[0].Hash[:]) + limit := "10" + + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Query chain + heaviestfromancestor := f.QueryHeaviestFromAncestor(ancestor, currentBest, validNewBest, limit) + + // Condition + actual := hex.EncodeToString(heaviestfromancestor.Res[:]) + suite.Equal(validNewBest, actual) + + // Require heaviestfromancestor fails with invalid params + f.QueryHeaviestFromAncestorInvalid("could not determine", ancestor, currentBest, invalidNewBest, limit) +} + +func (suite *UtilsSuite) TestRelayCLIQueryCheckProof() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + + // Ingest headers + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Require checkproof fails without headers associated with proof + checkProof := f.QueryCheckProof("1_check_proof.json", "--inputfile") + actual := checkProof.Valid + expected := false + suite.Equal(expected, actual) + + // Ingest associated header + success, stdout, stderr = f.TxIngestHeaders(fooAddr, "2_ingest_headers.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Require proof is valid when associated header exists with valid transaction + checkProof = f.QueryCheckProof("1_check_proof.json", "--inputfile") + expected = true + actual = checkProof.Valid + suite.Equal(expected, actual) +} + +func (suite *UtilsSuite) TestRelayCLITXIngestHeaders() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // define parameter valuse + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + + // Require IngestDiffChange fails with invalid headers + success, stdout, stderr := f.TxIngestHeaders(fooAddr, "2_ingest_headers.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":false`) + + //Ingest Difficulty Change Headers + success, stdout, stderr = f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Require successful IngestDiffChange with valid headers + success, stdout, stderr = f.TxIngestHeaders(fooAddr, "2_ingest_headers.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLITXIngestDiffChange() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // Define parameter values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + + // Require IngestDiffChange fails with invalid headers + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "2_ingest_headers.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":false`) + + // Require successful IngestDiffChange with valid headers + success, stdout, stderr = f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLITXProvideProof() { + suite.T().Parallel() + genesisHeaders := suite.TestData.GenesisHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // Define parameter values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + + // Ingest Headers w/ Diff Change + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // Ingest New Headers + success, stdout, stderr = f.TxIngestHeaders(fooAddr, "2_ingest_headers.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // require checkproof fails given invalid proof requests + _, stdout, _ = f.TxProvideProof(fooAddr, "1_check_proof.json", "3_filled_requests.json", "--inputfile -y") + suite.Contains(stdout, `"Request not found`) + + // submit proof request + spends := "0x" + pays := "0x17a91423737cd98bb6b2da5a11bcd82e5de36591d69f9f87" + value := "0" + numConfs := "1" + success, stdout, stderr = f.TxNewRequest(fooAddr, spends, pays, value, numConfs, "-y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // checkproof succeeds given valid proof and requests + success, stdout, stderr = f.TxProvideProof(fooAddr, "1_check_proof.json", "3_filled_requests.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + //Cleanup + f.Cleanup() +} + +func (suite *UtilsSuite) TestRelayCLITxMarkNewHeaviest() { + suite.T().Parallel() + + genesisHeaders := suite.TestData.GenesisHeaders + newDiffHeaders := suite.TestData.NewDiffHeaders + newHeaders := suite.TestData.NewHeaders + + // Initialize chain + f := InitFixtures(suite.T()) + proc := f.RelayDStart() + defer func() { + err := proc.Stop(false) + suite.NoError(err) + }() + + // Define parameter values + fooAddr := f.KeyAddress(keyFoo) + prevEpochStart := hex.EncodeToString(genesisHeaders[0].Hash[:]) + ancestor := hex.EncodeToString(genesisHeaders[1].Hash[:]) + bestKnown := hex.EncodeToString(genesisHeaders[1].Raw[:]) + invalidBestKnown := hex.EncodeToString(newHeaders[1].Raw[:]) + newBest := hex.EncodeToString(newDiffHeaders[1].Raw[:]) + limit := "10" + + // Ingest new headers + success, stdout, stderr := f.TxIngestDiffChange(fooAddr, prevEpochStart, "0_new_difficulty.json", "--inputfile -y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + // MarkNewHeaviest fails given invalid block + success, stdout, stderr = f.TxMarkNewHeaviest(fooAddr, ancestor, invalidBestKnown, newBest, limit, "-y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":false`) + + // Mark new heaviest digest + success, stdout, stderr = f.TxMarkNewHeaviest(fooAddr, ancestor, bestKnown, newBest, limit, "-y") + suite.True(success, stderr) + suite.Contains(stdout, `"success":true`) + + bestDigest := f.QueryGetBestDigest(fooAddr) + + // Condition + expected := hex.EncodeToString(newDiffHeaders[1].Hash[:]) + actual := hex.EncodeToString(bestDigest.Res[:]) + suite.Equal(expected, actual) + + //Cleanup + f.Cleanup() +} diff --git a/golang/cli_test/test_helpers.go b/golang/cli_test/test_helpers.go new file mode 100644 index 00000000..e68e4c8d --- /dev/null +++ b/golang/cli_test/test_helpers.go @@ -0,0 +1,451 @@ +package clitest + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + app "github.com/summa-tx/relays/golang" + rtypes "github.com/summa-tx/relays/golang/x/relay/types" + + clientkeys "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/tests" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + keyFoo = "foo" + stakeDenom = "stake" + fooDenom = "footoken" +) + +var startCoins = sdk.NewCoins( + sdk.NewCoin(stakeDenom, sdk.TokensFromConsensusPower(1000000)), + sdk.NewCoin(fooDenom, sdk.TokensFromConsensusPower(1000000)), +) + +type TestData struct { + GenesisHeaders []rtypes.BitcoinHeader + NewDiffHeaders []rtypes.BitcoinHeader + NewHeaders []rtypes.BitcoinHeader +} + +// Fixtures is used to setup the testing environment +type Fixtures struct { + BinDir string + RootDir string + RelaydBinary string + RelaycliBinary string + ChainID string + RPCAddr string + Port string + RelaydHome string + RelaycliHome string + P2PAddr string + T *testing.T +} + +////////////////////////////////////////////////////////////////////////////////////// +// Instantiation ///////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// NewFixtures creates a new instance of Fixtures with many vars set +func NewFixtures(t *testing.T) *Fixtures { + tmpDir, err := ioutil.TempDir("", "relay_integration_"+strings.TrimPrefix(t.Name(), "TestRelay/")+"_") + require.NoError(t, err) + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + + p2pAddr, _, err := server.FreeTCPAddr() + require.NoError(t, err) + + binDir := os.Getenv("GOPATH") + "/bin/" + require.True(t, fileExists(binDir+"relayd"), "relayd binary does not exist") + require.True(t, fileExists(binDir+"relaycli"), "relaycli binary does not exist") + + return &Fixtures{ + T: t, + BinDir: binDir, + RootDir: tmpDir, + RelaydBinary: filepath.Join(binDir, "relayd"), + RelaycliBinary: filepath.Join(binDir, "relaycli"), + RelaydHome: filepath.Join(tmpDir, ".relayd"), + RelaycliHome: filepath.Join(tmpDir, ".relaycli"), + RPCAddr: servAddr, + P2PAddr: p2pAddr, + Port: port, + } +} + +// InitFixtures is called at the beginning of a test and initializes a chain +// with 1 validator. +func InitFixtures(t *testing.T) (f *Fixtures) { + f = NewFixtures(t) + + // reset test state + f.UnsafeResetAll() + + f.KeysAdd(keyFoo) + + // ensure that CLI output is in JSON format + f.CLIConfig("output", "json") + + // NOTE: RelayDInit sets the ChainID + f.RelayDInit(keyFoo) + + f.CLIConfig("chain-id", f.ChainID) + f.CLIConfig("broadcast-mode", "block") + f.CLIConfig("trust-node", "true") + + // start an account with tokens + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + + f.GenTx(keyFoo) + f.CollectGenTxs() + + return f +} + +func GrabTestData(t *testing.T) TestData { + testData := TestData{} + genesisJSON := readJSONFile(t, "genesis") + err := json.Unmarshal([]byte(genesisJSON), &testData.GenesisHeaders) + require.NoError(t, err) + + newDiffJSON := readJSONFile(t, "0_new_difficulty") + err = json.Unmarshal([]byte(newDiffJSON), &testData.NewDiffHeaders) + require.NoError(t, err) + + newHeadersJSON := readJSONFile(t, "2_ingest_headers") + err = json.Unmarshal([]byte(newHeadersJSON), &testData.NewHeaders) + require.NoError(t, err) + + return testData +} + +////////////////////////////////////////////////////////////////////////////////////// +// Fixtures Interface //////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// RelayDInit is relayd init +// NOTE: RelayDInit sets the ChainID for the Fixtures instance +func (f *Fixtures) RelayDInit(moniker string, flags ...string) { + cmd := fmt.Sprintf("%s init -o --home=%s %s", f.RelaydBinary, f.RelaydHome, moniker) + _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) + + var chainID string + var initRes map[string]json.RawMessage + + err := json.Unmarshal([]byte(stderr), &initRes) + require.NoError(f.T, err) + + err = json.Unmarshal(initRes["chain_id"], &chainID) + require.NoError(f.T, err) + + f.ChainID = chainID +} + +// RelaydStart runs relayd start with the appropriate flags and returns a process +func (f *Fixtures) RelayDStart(flags ...string) *tests.Process { + cmd := fmt.Sprintf("%s start --home=%s --rpc.laddr=%v --p2p.laddr=%v", f.RelaydBinary, f.RelaydHome, f.RPCAddr, f.P2PAddr) + proc := tests.GoExecuteTWithStdout(f.T, addFlags(cmd, flags)) + tests.WaitForTMStart(f.Port) + tests.WaitForNextNBlocksTM(1, f.Port) + return proc +} + +// Cleanup is meant to be run at the end of a test to clean up an remaining test state +func (f *Fixtures) Cleanup(dirs ...string) { + clean := append(dirs, f.RootDir) + for _, d := range clean { + require.NoError(f.T, os.RemoveAll(d)) + } +} + +// AddGenesisAccount is relayd add-genesis-account +func (f *Fixtures) AddGenesisAccount(address sdk.AccAddress, coins sdk.Coins, flags ...string) { + cmd := fmt.Sprintf("%s add-genesis-account %s %s --home=%s", f.RelaydBinary, address, coins, f.RelaydHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +// GenesisFile returns the path of the generated genesis file +func (f Fixtures) GenesisFile() string { + return filepath.Join(f.RelaydHome, "config", "genesis.json") +} + +// Flags returns the flags necessary for making most CLI calls +func (f *Fixtures) Flags() string { + return fmt.Sprintf("--home=%s --node=%s", f.RelaycliHome, f.RPCAddr) +} + +// KeysAdd is relaycli keys add +func (f *Fixtures) KeysAdd(name string, flags ...string) { + cmd := fmt.Sprintf("%s keys add --home=%s %s", f.RelaycliBinary, f.RelaycliHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// CLIConfig is relaycli config +func (f *Fixtures) CLIConfig(key, value string, flags ...string) { + cmd := fmt.Sprintf("%s config --home=%s %s %s", f.RelaycliBinary, f.RelaycliHome, key, value) + executeWriteCheckErr(f.T, addFlags(cmd, flags)) +} + +// CollectGenTxs is relayd collect-gentxs +func (f *Fixtures) CollectGenTxs(flags ...string) { + cmd := fmt.Sprintf("%s collect-gentxs --home=%s", f.RelaydBinary, f.RelaydHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// KeysShow is relaycli keys show +func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { + cmd := fmt.Sprintf("%s keys show --home=%s %s", f.RelaycliBinary, + f.RelaycliHome, name) + out, _ := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + var ko keys.KeyOutput + err := clientkeys.UnmarshalJSON([]byte(out), &ko) + require.NoError(f.T, err) + return ko +} + +// UnsafeResetAll is relayd unsafe-reset-all +func (f *Fixtures) UnsafeResetAll(flags ...string) { + cmd := fmt.Sprintf("%s --home=%s unsafe-reset-all", f.RelaydBinary, f.RelaydHome) + executeWrite(f.T, addFlags(cmd, flags)) + err := os.RemoveAll(filepath.Join(f.RelaydHome, "config", "gentx")) + require.NoError(f.T, err) +} + +// KeyAddress returns the SDK account address from the key +func (f *Fixtures) KeyAddress(name string) sdk.AccAddress { + ko := f.KeysShow(name) + accAddr, err := sdk.AccAddressFromBech32(ko.Address) + require.NoError(f.T, err) + return accAddr +} + +// GenTx is relayd gentx +func (f *Fixtures) GenTx(name string, flags ...string) { + cmd := fmt.Sprintf("%s gentx --name=%s --home=%s --home-client=%s", f.RelaydBinary, name, f.RelaydHome, f.RelaycliHome) + executeWriteCheckErr(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +////////////////////////////////////////////////////////////////////////////////////// +// CLI Queries /////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +// QueryGetRelayGenesis returns the relay genesis block Hash +func (f *Fixtures) QueryGetRelayGenesis(delAddr sdk.AccAddress) rtypes.QueryResGetRelayGenesis { + cmd := fmt.Sprintf("%s query relay getrelaygenesis %s %s", f.RelaycliBinary, delAddr, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var relaygenesis rtypes.QueryResGetRelayGenesis + err := cdc.UnmarshalJSON([]byte(res), &relaygenesis) + require.NoError(f.T, err) + return relaygenesis +} + +// QueryGetLastReorgLCA returns Last Common Anscestor +func (f *Fixtures) QueryGetLastReorgLCA(delAddr sdk.AccAddress) rtypes.QueryResGetLastReorgLCA { + cmd := fmt.Sprintf("%s query relay getlastreorglca %s %s", f.RelaycliBinary, delAddr, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var lastreorglca rtypes.QueryResGetLastReorgLCA + err := cdc.UnmarshalJSON([]byte(res), &lastreorglca) + require.NoError(f.T, err) + return lastreorglca +} + +// QueryGetBestDigest returns the Best Known Digest +func (f *Fixtures) QueryGetBestDigest(delAddr sdk.AccAddress) rtypes.QueryResGetBestDigest { + cmd := fmt.Sprintf("%s query relay getbestdigest %s %s", f.RelaycliBinary, delAddr, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var bestknowndigest rtypes.QueryResGetBestDigest + err := cdc.UnmarshalJSON([]byte(res), &bestknowndigest) + require.NoError(f.T, err) + return bestknowndigest +} + +// QueryIsAncestor returns the Boolean +func (f *Fixtures) QueryIsAncestor(digest, ancestor, limit string) rtypes.QueryResIsAncestor { + cmd := fmt.Sprintf("%s query relay isancestor %s %s %s %s", f.RelaycliBinary, digest, ancestor, limit, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var isancestor rtypes.QueryResIsAncestor + err := cdc.UnmarshalJSON([]byte(res), &isancestor) + require.NoError(f.T, err) + return isancestor +} + +// QueryFindAncestor returns the Boolean +func (f *Fixtures) QueryFindAncestor(digest, offset string) rtypes.QueryResFindAncestor { + cmd := fmt.Sprintf("%s query relay findancestor %s %s %s", f.RelaycliBinary, digest, offset, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var findancestor rtypes.QueryResFindAncestor + err := cdc.UnmarshalJSON([]byte(res), &findancestor) + require.NoError(f.T, err) + return findancestor +} + +// QueryFindAncestorInvalid require proper response for invalid query +func (f *Fixtures) QueryFindAncestorInvalid(errStr, digest, offset string) { + cmd := fmt.Sprintf("%s query relay findancestor %s %s %s", f.RelaycliBinary, digest, offset, f.Flags()) + res, _ := tests.ExecuteT(f.T, cmd, "") + require.Contains(f.T, res, errStr) +} + +// QueryIsMostRecentCommonAncestor returns a Boolean +func (f *Fixtures) QueryIsMostRecentCommonAncestor(ancestor, left, right, limit string) rtypes.QueryResIsMostRecentCommonAncestor { + cmd := fmt.Sprintf("%s query relay ismostrecentcommonancestor %s %s %s %s %s", f.RelaycliBinary, ancestor, left, right, limit, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var ismostrecentcommonancestor rtypes.QueryResIsMostRecentCommonAncestor + err := cdc.UnmarshalJSON([]byte(res), &ismostrecentcommonancestor) + require.NoError(f.T, err) + return ismostrecentcommonancestor +} + +// QueryHeaviestFromAncestor returns a Boolean +func (f *Fixtures) QueryHeaviestFromAncestor(ancestor, currentBest, newBest, limit string) rtypes.QueryResHeaviestFromAncestor { + cmd := fmt.Sprintf("%s query relay heaviestfromancestor %s %s %s %s %s", f.RelaycliBinary, ancestor, currentBest, newBest, limit, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var heaviestfromancestor rtypes.QueryResHeaviestFromAncestor + err := cdc.UnmarshalJSON([]byte(res), &heaviestfromancestor) + require.NoError(f.T, err) + return heaviestfromancestor +} + +// QueryHeaviestFromAncestorInvalid require proper response for invalid query +func (f *Fixtures) QueryHeaviestFromAncestorInvalid(errStr, ancestor, currentBest, newBest, limit string) { + cmd := fmt.Sprintf("%s query relay heaviestfromancestor %s %s %s %s %s", f.RelaycliBinary, ancestor, currentBest, newBest, limit, f.Flags()) + res, _ := tests.ExecuteT(f.T, cmd, "") + require.Contains(f.T, res, errStr) +} + +// QueryCheckProof returns the Boolean +func (f *Fixtures) QueryCheckProof(proof string, flags ...string) rtypes.QueryResCheckProof { + cmd := fmt.Sprintf("%s query relay checkproof %s %s", f.RelaycliBinary, proof, f.Flags()) + res, errStr := tests.ExecuteT(f.T, addFlags(cmd, flags), "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var checkproof rtypes.QueryResCheckProof + err := cdc.UnmarshalJSON([]byte(res), &checkproof) + require.NoError(f.T, err) + return checkproof +} + +///////////////////////////////////////////////////////////////////// +// CLI Transactions ///////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// + +// TxIngestDiffChange is relaycli tx that ingests headers with new difficulty +func (f *Fixtures) TxIngestDiffChange(delAddr sdk.AccAddress, prevEpochStart, jsonHeaders string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx relay ingestdiffchange %s %s --from %s %s", f.RelaycliBinary, prevEpochStart, jsonHeaders, delAddr, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// TxIngestHeaders is a relaycli tx that ingests headers with same difficulty as previous headers +func (f *Fixtures) TxIngestHeaders(delAddr sdk.AccAddress, headers string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx relay ingestheaders %s --from %s %s", f.RelaycliBinary, headers, delAddr, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// TxNewRequest is a relaycli tx that submits a new Proof Request +func (f *Fixtures) TxNewRequest(delAddr sdk.AccAddress, spends, pays, value, numConfs string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx relay newrequest %s %s %s %s --from %s %s", f.RelaycliBinary, spends, pays, value, numConfs, delAddr, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// TxProvideProof is a relaycli tx that submits a new Proof Request +func (f *Fixtures) TxProvideProof(delAddr sdk.AccAddress, proof, listofrequests string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx relay provideproof %s %s --from %s %s", f.RelaycliBinary, proof, listofrequests, delAddr, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +// TxMarkNewHeaviest returns Last Common Anscestor +func (f *Fixtures) TxMarkNewHeaviest(delAddr sdk.AccAddress, ancestor, currentBest, newBest, limit string, flags ...string) (bool, string, string) { + cmd := fmt.Sprintf("%s tx relay marknewheaviest %s %s %s %s --from %s %s", f.RelaycliBinary, ancestor, currentBest, newBest, limit, delAddr, f.Flags()) + return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), clientkeys.DefaultKeyPass) +} + +////////////////////////////////////////////////////////////////////////////////////// +// Executors ///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// + +func executeWriteCheckErr(t *testing.T, cmdStr string, writes ...string) { + require.True(t, executeWrite(t, cmdStr, writes...)) +} + +func executeWrite(t *testing.T, cmdStr string, writes ...string) (exitSuccess bool) { + exitSuccess, _, _ = executeWriteRetStdStreams(t, cmdStr, writes...) + return +} + +func executeWriteRetStdStreams(t *testing.T, cmdStr string, writes ...string) (bool, string, string) { + proc := tests.GoExecuteT(t, cmdStr) + + // Enables use of interactive commands + for _, write := range writes { + _, err := proc.StdinPipe.Write([]byte(write + "\n")) + require.NoError(t, err) + } + + // Read both stdout and stderr from the process + stdout, stderr, err := proc.ReadAll() + if err != nil { + fmt.Println("Err on proc.ReadAll()", err, cmdStr) + } + + // Log output. + if len(stdout) > 0 { + t.Log("Stdout:", string(stdout)) + } + if len(stderr) > 0 { + t.Log("Stderr:", string(stderr)) + } + + // Wait for process to exit + proc.Wait() + // Return succes, stdout, stderr + return proc.ExitState.Success(), string(stdout), string(stderr) +} + +////////////////////////////////////////////////////////////////////////////////////// +// utils ///////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////// +func addFlags(cmd string, flags []string) string { + for _, f := range flags { + cmd += " " + f + } + return strings.TrimSpace(cmd) +} + +func readJSONFile(t *testing.T, filename string) []byte { + headerJSON, jsonErr := ioutil.ReadFile("../scripts/json_data/" + filename + ".json") + require.NoError(t, jsonErr) + return headerJSON +} + +func fileExists(filename string) bool { + info, err := os.Stat(filename) + if os.IsNotExist(err) { + return false + } + return !info.IsDir() +} diff --git a/golang/cmd/relaycli/main.go b/golang/cmd/relaycli/main.go index 4d98707e..935a26c2 100644 --- a/golang/cmd/relaycli/main.go +++ b/golang/cmd/relaycli/main.go @@ -32,7 +32,7 @@ func main() { config.Seal() rootCmd := &cobra.Command{ - Use: "rcli", + Use: "relaycli", Short: "relay Client", } diff --git a/golang/scripts/json_data/0_new_difficulty.json b/golang/scripts/json_data/0_new_difficulty.json index 6f73a148..179f7d66 100644 --- a/golang/scripts/json_data/0_new_difficulty.json +++ b/golang/scripts/json_data/0_new_difficulty.json @@ -1,22 +1,16 @@ [ { "raw": "0000ff3f4c2078d0388e3844fe6241723e9543074bd3a974c166110000000000000000000b43965e06a087b47726f35aa886df5630057215bb46a58216243e236565ac296053425ed41a121781b98236", - "hash": "00000000000000000004f0d5a56473157602071a4bb112623b1211536d40e69b", - "hash_le": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", + "hash": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", "height": 616896, - "prevhash": "0000000000000000001166c174a9d34b0743953e724162fe44388e38d078204c", - "prevhash_le": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", - "merkle_root": "29ac6565233e241682a546bb1572053056df86a85af32677b487a0065e96430b", - "merkle_root_le": "0b43965e06a087b47726f35aa886df5630057215bb46a58216243e236565ac29" + "prevhash": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", + "merkle_root": "0b43965e06a087b47726f35aa886df5630057215bb46a58216243e236565ac29" }, { "raw": "00c0ff3f9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000f19f16556e7952046ea684eb6e8aeebe5fc1bb7fca09a0633fd3da5ee9e2dbb80c56425ed41a12172fdc7d2c", - "hash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", - "hash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "hash": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", "height": 616897, - "prevhash": "00000000000000000004f0d5a56473157602071a4bb112623b1211536d40e69b", - "prevhash_le": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", - "merkle_root": "b8dbe2e95edad33f63a009ca7fbbc15fbeee8a6eeb84a66e0452796e55169ff1", - "merkle_root_le": "f19f16556e7952046ea684eb6e8aeebe5fc1bb7fca09a0633fd3da5ee9e2dbb8" + "prevhash": "9be6406d5311123b6212b14b1a070276157364a5d5f004000000000000000000", + "merkle_root": "f19f16556e7952046ea684eb6e8aeebe5fc1bb7fca09a0633fd3da5ee9e2dbb8" } ] diff --git a/golang/scripts/json_data/1_check_proof.json b/golang/scripts/json_data/1_check_proof.json index b984ed5a..ecd77920 100644 --- a/golang/scripts/json_data/1_check_proof.json +++ b/golang/scripts/json_data/1_check_proof.json @@ -3,18 +3,14 @@ "vin": "01bbfe169156ef6922af60eced842fd588db83b1803d75f94b3054ab20c36729c20000000017160014761503a7b541c34ab017fe95d4e56b872af45319feffffff", "vout": "023a44090a0000000017a91423737cd98bb6b2da5a11bcd82e5de36591d69f9f87b0c412000000000017a9143b77f8e86b4b65b4173a6a489b067dc2d14e49ce87", "locktime": "c1690900", - "tx_id": "7dfc00bb2873727ee2bcb9ae448e90561c3476afeb04fabfeb48b0f9837d14f2", - "tx_id_le": "f2147d83f9b048ebbffa04ebaf76341c56908e44aeb9bce27e727328bb00fc7d", + "tx_id": "f2147d83f9b048ebbffa04ebaf76341c56908e44aeb9bce27e727328bb00fc7d", "index": 13, "confirming_header": { "raw": "00e000200641238051855d1759da9b6603b156684a68a146d36a09000000000000000000f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4be58425ed41a1217d632cdcc", - "hash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", - "hash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "hash": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", "height": 616898, - "prevhash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", - "prevhash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", - "merkle_root": "a4ab3f68a57d5d743b5a02bfb551baed59a9fc17281605b886a2ae47f3d7f5f1", - "merkle_root_le": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" + "prevhash": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "merkle_root": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" }, "intermediate_nodes": "58088fd963c83b6f8605a2e36230039f2f0327838984a439fbd02a747068f1394e0592da283da5fec881377e14a6a4952b8aafe69eef0f79249228b8d3774b21047d13d55d8565a84c7ec5bb66e677a322a197bd582eb8033163560a63423e919ecfe60a2c3d5a16e82faf72b1c5318a1b4c9fbc33945980b45b0b703d8c3cf4892ee4da7182112658c372a27adafae0588bf12242c7ee10eac9e2ca406b92cfd23133c8b957b6f52ff2c25b836b34c3587ea0c756cb994072b44199a718d913ab51c5c74aaab591cc846370692b17a7378c8aee6e1d46020d04b00208ab9fe0a545ef7072d6304c15005a2c3d4d77c94f95d2c2fa8bc00894d0eea0b7dda08333e53e219616c479448c43bf7df9463f600eac9380d257fdb85b64d3a627f335a535867e0f5d20a39a9ae95c55ab6ffddecc94530a95bd877b6583a37d635f2f8ff007761bc8623ce3a1bb93cbd1f65acaadbf419a56abd5ce00a94305ea8b5fcfa5f5e0bb8ae134a445e4ff3c650be2bdc2d07f8ad65670ccf6b0077e90f5b7" } diff --git a/golang/scripts/json_data/2_ingest_headers.json b/golang/scripts/json_data/2_ingest_headers.json index 1ae3d72f..879a145b 100644 --- a/golang/scripts/json_data/2_ingest_headers.json +++ b/golang/scripts/json_data/2_ingest_headers.json @@ -1,112 +1,79 @@ [ { "raw": "00e000200641238051855d1759da9b6603b156684a68a146d36a09000000000000000000f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4be58425ed41a1217d632cdcc", - "hash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", - "hash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "hash": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", "height": 616898, - "prevhash": "000000000000000000096ad346a1684a6856b103669bda59175d855180234106", - "prevhash_le": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", - "merkle_root": "a4ab3f68a57d5d743b5a02bfb551baed59a9fc17281605b886a2ae47f3d7f5f1", - "merkle_root_le": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" + "prevhash": "0641238051855d1759da9b6603b156684a68a146d36a09000000000000000000", + "merkle_root": "f1f5d7f347aea286b805162817fca959edba51b5bf025a3b745d7da5683faba4" }, { "raw": "0000ff3f7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf090000000000000000000c2aeb41cdea44a03b437fcd294805092c294d613f7f7192e6ffc9c6ec9130cddf58425ed41a121783dd5b80", - "hash": "00000000000000000002c7b080fec48d40ce8d686e2cf0d011ddb8c648326b39", - "hash_le": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", + "hash": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", "height": 616899, - "prevhash": "00000000000000000009af0e65a9578b47d75c0aa8b45480a0d63a1ddb23997f", - "prevhash_le": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", - "merkle_root": "cd3091ecc6c9ffe692717f3f614d292c09054829cd7f433ba044eacd41eb2a0c", - "merkle_root_le": "0c2aeb41cdea44a03b437fcd294805092c294d613f7f7192e6ffc9c6ec9130cd" + "prevhash": "7f9923db1d3ad6a08054b4a80a5cd7478b57a9650eaf09000000000000000000", + "merkle_root": "0c2aeb41cdea44a03b437fcd294805092c294d613f7f7192e6ffc9c6ec9130cd" }, { "raw": "00e0ff3f396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c70200000000000000000033b53cacf908eb0763918b833b80936d88c3bfa6999c280450d03855208791691f5a425ed41a1217940afef1", - "hash": "0000000000000000000cb8dcb46277f11dc04f0f34c18e8e2ee4bf8f1f9a2413", - "hash_le": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", + "hash": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", "height": 616900, - "prevhash": "00000000000000000002c7b080fec48d40ce8d686e2cf0d011ddb8c648326b39", - "prevhash_le": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", - "merkle_root": "699187205538d05004289c99a6bfc3886d93803b838b916307eb08f9ac3cb533", - "merkle_root_le": "33b53cacf908eb0763918b833b80936d88c3bfa6999c280450d0385520879169" + "prevhash": "396b3248c6b8dd11d0f02c6e688dce408dc4fe80b0c702000000000000000000", + "merkle_root": "33b53cacf908eb0763918b833b80936d88c3bfa6999c280450d0385520879169" }, { "raw": "00e0002013249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000a9a61a4be4289169e82e9ba5ac35b9680cb6f9cb4bea0ae5b1b2ab6cda7eaac6745b425ed41a1217ddcacc90", - "hash": "000000000000000000025ad6dccc7192e070961b1dc011edb653f43765ba8888", - "hash_le": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", + "hash": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", "height": 616901, - "prevhash": "0000000000000000000cb8dcb46277f11dc04f0f34c18e8e2ee4bf8f1f9a2413", - "prevhash_le": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", - "merkle_root": "c6aa7eda6cabb2b1e50aea4bcbf9b60c68b935aca59b2ee8699128e44b1aa6a9", - "merkle_root_le": "a9a61a4be4289169e82e9ba5ac35b9680cb6f9cb4bea0ae5b1b2ab6cda7eaac6" + "prevhash": "13249a1f8fbfe42e8e8ec1340f4fc01df17762b4dcb80c000000000000000000", + "merkle_root": "a9a61a4be4289169e82e9ba5ac35b9680cb6f9cb4bea0ae5b1b2ab6cda7eaac6" }, { "raw": "00e0ff3f8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000b2988e5fd8ef3af6aa08e1724147bd371c572bf80d86ad78a2bb3350f675c3a27d62425ed41a1217d68e19fd", - "hash": "0000000000000000000cdb8e560dfaa995928e2621fdb85e017996093baa87ea", - "hash_le": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", + "hash": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", "height": 616902, - "prevhash": "000000000000000000025ad6dccc7192e070961b1dc011edb653f43765ba8888", - "prevhash_le": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", - "merkle_root": "a2c375f65033bba278ad860df82b571c37bd474172e108aaf63aefd85f8e98b2", - "merkle_root_le": "b2988e5fd8ef3af6aa08e1724147bd371c572bf80d86ad78a2bb3350f675c3a2" + "prevhash": "8888ba6537f453b6ed11c01d1b9670e09271ccdcd65a02000000000000000000", + "merkle_root": "b2988e5fd8ef3af6aa08e1724147bd371c572bf80d86ad78a2bb3350f675c3a2" }, { "raw": "00008020ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c0000000000000000008ebb3bf274b7ab7f299279f3086dd59f5d45e4dc116db4d470c38dd2ff2b2c8f5b65425ed41a12174bd195f8", - "hash": "0000000000000000000b8369116e8168ecd130c9dcc44e6d10c5dcb92cb45547", - "hash_le": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", + "hash": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", "height": 616903, - "prevhash": "0000000000000000000cdb8e560dfaa995928e2621fdb85e017996093baa87ea", - "prevhash_le": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", - "merkle_root": "8f2c2bffd28dc370d4b46d11dce4455d9fd56d08f37992297fabb774f23bbb8e", - "merkle_root_le": "8ebb3bf274b7ab7f299279f3086dd59f5d45e4dc116db4d470c38dd2ff2b2c8f" + "prevhash": "ea87aa3b099679015eb8fd21268e9295a9fa0d568edb0c000000000000000000", + "merkle_root": "8ebb3bf274b7ab7f299279f3086dd59f5d45e4dc116db4d470c38dd2ff2b2c8f" }, { "raw": "0000c0204755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b00000000000000000040fd126a7fac3d39cd65b7aa87df4785fe3e585eac589a56578cb4e6a42824bdae68425ed41a1217155cc584", - "hash": "0000000000000000000d8fc9184374dfdc808fc5a7e3409d9e42ed06a5a002ab", - "hash_le": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", + "hash": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", "height": 616904, - "prevhash": "0000000000000000000b8369116e8168ecd130c9dcc44e6d10c5dcb92cb45547", - "prevhash_le": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", - "merkle_root": "bd2428a4e6b48c57569a58ac5e583efe8547df87aab765cd393dac7f6a12fd40", - "merkle_root_le": "40fd126a7fac3d39cd65b7aa87df4785fe3e585eac589a56578cb4e6a42824bd" + "prevhash": "4755b42cb9dcc5106d4ec4dcc930d1ec68816e1169830b000000000000000000", + "merkle_root": "40fd126a7fac3d39cd65b7aa87df4785fe3e585eac589a56578cb4e6a42824bd" }, { "raw": "00e0ff7fab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000d338bb7242ca738d8cb0ef0f05c4d888c52eb5a075041ffafbabc3268f041544166b425ed41a12178cd64a96", - "hash": "0000000000000000000962d551d6269edda97e5ff94586c84514f8cc7de1b50e", - "hash_le": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", + "hash": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", "height": 616905, - "prevhash": "0000000000000000000d8fc9184374dfdc808fc5a7e3409d9e42ed06a5a002ab", - "prevhash_le": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", - "merkle_root": "4415048f26c3abfbfa1f0475a0b52ec588d8c4050fefb08c8d73ca4272bb38d3", - "merkle_root_le": "d338bb7242ca738d8cb0ef0f05c4d888c52eb5a075041ffafbabc3268f041544" + "prevhash": "ab02a0a506ed429e9d40e3a7c58f80dcdf744318c98f0d000000000000000000", + "merkle_root": "d338bb7242ca738d8cb0ef0f05c4d888c52eb5a075041ffafbabc3268f041544" }, { "raw": "00c0ff3f0eb5e17dccf81445c88645f95f7ea9dd9e26d651d5620900000000000000000064b53b8a3e102a1b7e7cbb245dd1339e87d34abcea730009bd2a2dca93683a71426f425ed41a12177b11d4dd", - "hash": "00000000000000000011de2a467a8d8dc58af1a6ba4e0b527edef805f83cfadd", - "hash_le": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", + "hash": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", "height": 616906, - "prevhash": "0000000000000000000962d551d6269edda97e5ff94586c84514f8cc7de1b50e", - "prevhash_le": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", - "merkle_root": "713a6893ca2d2abd090073eabc4ad3879e33d15d24bb7c7e1b2a103e8a3bb564", - "merkle_root_le": "64b53b8a3e102a1b7e7cbb245dd1339e87d34abcea730009bd2a2dca93683a71" + "prevhash": "0eb5e17dccf81445c88645f95f7ea9dd9e26d651d56209000000000000000000", + "merkle_root": "64b53b8a3e102a1b7e7cbb245dd1339e87d34abcea730009bd2a2dca93683a71" }, { "raw": "00e00020ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade110000000000000000000d3e06e5e25b12623a9d6cd9f2c0b6df31315b64934a1e9b61edcc90e9f4ae275870425ed41a1217a1371395", - "hash": "00000000000000000009d103820ebb8000991df70f5b4750880e1bfb0fd400bc", - "hash_le": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", + "hash": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", "height": 616907, - "prevhash": "00000000000000000011de2a467a8d8dc58af1a6ba4e0b527edef805f83cfadd", - "prevhash_le": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", - "merkle_root": "27aef4e990cced619b1e4a93645b3131dfb6c0f2d96c9d3a62125be2e5063e0d", - "merkle_root_le": "0d3e06e5e25b12623a9d6cd9f2c0b6df31315b64934a1e9b61edcc90e9f4ae27" + "prevhash": "ddfa3cf805f8de7e520b4ebaa6f18ac58d8d7a462ade11000000000000000000", + "merkle_root": "0d3e06e5e25b12623a9d6cd9f2c0b6df31315b64934a1e9b61edcc90e9f4ae27" }, { "raw": "0000c020bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d1090000000000000000008a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3b971425ed41a12174334a315", - "hash": "00000000000000000003756d91d26f6322212607bfb6e35d7e02e4bf38a0d0f8", - "hash_le": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", + "hash": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", "height": 616908, - "prevhash": "00000000000000000009d103820ebb8000991df70f5b4750880e1bfb0fd400bc", - "prevhash_le": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", - "merkle_root": "e3c7facde577ff104537a9a6658674e65feabcd61b744ced1030c57c377b318a", - "merkle_root_le": "8a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3" + "prevhash": "bc00d40ffb1b0e8850475b0ff71d990080bb0e8203d109000000000000000000", + "merkle_root": "8a317b377cc53010ed4c741bd6bcea5fe6748665a6a9374510ff77e5cdfac7e3" } ] diff --git a/golang/scripts/json_data/4_ingest_headers.json b/golang/scripts/json_data/4_ingest_headers.json index a40da1f5..5d1e9f6e 100644 --- a/golang/scripts/json_data/4_ingest_headers.json +++ b/golang/scripts/json_data/4_ingest_headers.json @@ -1,62 +1,44 @@ [ { "raw": "00e0ff2ff8d0a038bfe4027e5de3b6bf07262122636fd2916d75030000000000000000009c66fd29c230fbc348ba962f1c2ca8c6c9bca2cd01fd55006f31d65d0ed139016572425ed41a1217c6c10ed1", - "hash": "00000000000000000002405dc45827d64aabf25ac548984f4f7f94a89c4cda5e", - "hash_le": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", + "hash": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", "height": 616909, - "prevhash": "00000000000000000003756d91d26f6322212607bfb6e35d7e02e4bf38a0d0f8", - "prevhash_le": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", - "merkle_root": "0139d10e5dd6316f0055fd01cda2bcc9c6a82c1c2f96ba48c3fb30c229fd669c", - "merkle_root_le": "9c66fd29c230fbc348ba962f1c2ca8c6c9bca2cd01fd55006f31d65d0ed13901" + "prevhash": "f8d0a038bfe4027e5de3b6bf07262122636fd2916d7503000000000000000000", + "merkle_root": "9c66fd29c230fbc348ba962f1c2ca8c6c9bca2cd01fd55006f31d65d0ed13901" }, { "raw": "000000205eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d40020000000000000000002af75f1c0581dfaf0aa7007deec24ef232fd280d60a16a2ab26ee8dd509c746bcd74425ed41a1217f47350e8", - "hash": "00000000000000000007296d7e65357a8157c66d647703338f96305d0e0344f4", - "hash_le": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", + "hash": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", "height": 616910, - "prevhash": "00000000000000000002405dc45827d64aabf25ac548984f4f7f94a89c4cda5e", - "prevhash_le": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", - "merkle_root": "6b749c50dde86eb22a6aa1600d28fd32f24ec2ee7d00a70aafdf81051c5ff72a", - "merkle_root_le": "2af75f1c0581dfaf0aa7007deec24ef232fd280d60a16a2ab26ee8dd509c746b" + "prevhash": "5eda4c9ca8947f4f4f9848c55af2ab4ad62758c45d4002000000000000000000", + "merkle_root": "2af75f1c0581dfaf0aa7007deec24ef232fd280d60a16a2ab26ee8dd509c746b" }, { "raw": "00e00020f444030e5d30968f330377646dc657817a35657e6d29070000000000000000007ec6b452207ab96612f2b3231325877fa83b814a03e2069bac136f2f0ac42435e176425ed41a12173c2750a5", - "hash": "00000000000000000007ff64bae73f65f9b5b932b1e592eea27fc7b6bfcefe0e", - "hash_le": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", + "hash": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", "height": 616911, - "prevhash": "00000000000000000007296d7e65357a8157c66d647703338f96305d0e0344f4", - "prevhash_le": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", - "merkle_root": "3524c40a2f6f13ac9b06e2034a813ba87f87251323b3f21266b97a2052b4c67e", - "merkle_root_le": "7ec6b452207ab96612f2b3231325877fa83b814a03e2069bac136f2f0ac42435" + "prevhash": "f444030e5d30968f330377646dc657817a35657e6d2907000000000000000000", + "merkle_root": "7ec6b452207ab96612f2b3231325877fa83b814a03e2069bac136f2f0ac42435" }, { "raw": "00e000200efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff070000000000000000007d1740590d7b907cec752812c3202fed4f1c39ab565d27867981bc6b4d4a20b28578425ed41a12170984291f", - "hash": "00000000000000000010816dffafe360e8ad742120a77b9c477685e9587ab6f1", - "hash_le": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", + "hash": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", "height": 616912, - "prevhash": "00000000000000000007ff64bae73f65f9b5b932b1e592eea27fc7b6bfcefe0e", - "prevhash_le": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", - "merkle_root": "b2204a4d6bbc817986275d56ab391c4fed2f20c3122875ec7c907b0d5940177d", - "merkle_root_le": "7d1740590d7b907cec752812c3202fed4f1c39ab565d27867981bc6b4d4a20b2" + "prevhash": "0efecebfb6c77fa2ee92e5b132b9b5f9653fe7ba64ff07000000000000000000", + "merkle_root": "7d1740590d7b907cec752812c3202fed4f1c39ab565d27867981bc6b4d4a20b2" }, { "raw": "00000020f1b67a58e98576479c7ba7202174ade860e3afff6d81100000000000000000009293b2d0c8f430a39cc8346c5478df790027176b5d26b0893a088ff6179b63108a7a425ed41a12173041448e", - "hash": "0000000000000000000bbead2875713f0136e921d879bc958f31d39df37b6751", - "hash_le": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", + "hash": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", "height": 616913, - "prevhash": "00000000000000000010816dffafe360e8ad742120a77b9c477685e9587ab6f1", - "prevhash_le": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", - "merkle_root": "10639b17f68f083a89b0265d6b17270079df78546c34c89ca330f4c8d0b29392", - "merkle_root_le": "9293b2d0c8f430a39cc8346c5478df790027176b5d26b0893a088ff6179b6310" + "prevhash": "f1b67a58e98576479c7ba7202174ade860e3afff6d8110000000000000000000", + "merkle_root": "9293b2d0c8f430a39cc8346c5478df790027176b5d26b0893a088ff6179b6310" }, { "raw": "0000002051677bf39dd3318f95bc79d821e936013f717528adbe0b0000000000000000008bdd1ff50a88a852d497061c51542db436818bd3b6b2455c0f6333d7533cffd1387c425ed41a12175f902567", - "hash": "000000000000000000104f6120e4f469427f347555b1f37251fe90f94b33f0e6", - "hash_le": "e6f0334bf990fe5172f3b15575347f4269f4e420614f10000000000000000000", + "hash": "e6f0334bf990fe5172f3b15575347f4269f4e420614f10000000000000000000", "height": 616914, - "prevhash": "0000000000000000000bbead2875713f0136e921d879bc958f31d39df37b6751", - "prevhash_le": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", - "merkle_root": "d1ff3c53d733630f5c45b2b6d38b8136b42d54511c0697d452a8880af51fdd8b", - "merkle_root_le": "8bdd1ff50a88a852d497061c51542db436818bd3b6b2455c0f6333d7533cffd1" + "prevhash": "51677bf39dd3318f95bc79d821e936013f717528adbe0b000000000000000000", + "merkle_root": "8bdd1ff50a88a852d497061c51542db436818bd3b6b2455c0f6333d7533cffd1" } ] diff --git a/golang/scripts/json_data/genesis.json b/golang/scripts/json_data/genesis.json index ea2efdfa..6def766a 100644 --- a/golang/scripts/json_data/genesis.json +++ b/golang/scripts/json_data/genesis.json @@ -1,21 +1,15 @@ [ { "raw": "00c0ff3fcc235a9993cbdfe938b54b9850485c46b6a408dd23fe100000000000000000006d79472dd0cf809ff632565b6de2fb051f5f87a06a5daf79bb95412f357f434353f42f5eff32121761139949", - "hash": "00000000000000000004fec124eaa3f2e893d2cc26a7c22a547b270b824882ef", - "hash_le": "ef8248820b277b542ac2a726ccd293e8f2a3ea24c1fe04000000000000000000", + "hash": "ef8248820b277b542ac2a726ccd293e8f2a3ea24c1fe04000000000000000000", "height": 614880, - "prevhash": "00000000000000000010fe23dd08a4b6465c4850984bb538e9dfcb93995a23cc", - "prevhash_le": "cc235a9993cbdfe938b54b9850485c46b6a408dd23fe10000000000000000000", - "merkle_root": "43437f352f4195bb79af5d6aa0875f1f05fbe26d5b5632f69f80cfd02d47796d", - "merkle_root_le": "6d79472dd0cf809ff632565b6de2fb051f5f87a06a5daf79bb95412f357f4343"}, + "prevhash": "cc235a9993cbdfe938b54b9850485c46b6a408dd23fe10000000000000000000", + "merkle_root": "6d79472dd0cf809ff632565b6de2fb051f5f87a06a5daf79bb95412f357f4343"}, { "raw": "0000c020954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618dd150425eff3212173f0c982d", - "hash": "0000000000000000001166c174a9d34b0743953e724162fe44388e38d078204c", - "hash_le": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", + "hash": "4c2078d0388e3844fe6241723e9543074bd3a974c16611000000000000000000", "height": 616895, - "prevhash": "0000000000000000000c9610f5cd595f405a025e2060c2d54fc3ab80d9a14e95", - "prevhash_le": "954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000", - "merkle_root": "8d61daf6f04474eda372ab2ad7e74679a0d36d93c445da97e514caa6044d86ad", - "merkle_root_le": "ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618d" + "prevhash": "954ea1d980abc34fd5c260205e025a405f59cdf510960c000000000000000000", + "merkle_root": "ad864d04a6ca14e597da45c4936dd3a07946e7d72aab72a3ed7444f0f6da618d" } ] diff --git a/golang/x/relay/client/cli/utils.go b/golang/x/relay/client/cli/utils.go index 357e4d3a..0f0c1806 100644 --- a/golang/x/relay/client/cli/utils.go +++ b/golang/x/relay/client/cli/utils.go @@ -3,10 +3,19 @@ package cli import ( "github.com/spf13/cobra" "io/ioutil" + "os" + "strings" ) func readJSONFromFile(filename string) ([]byte, error) { - return ioutil.ReadFile("scripts/json_data/" + filename) + // get path to root directory + path, err := os.Getwd() + if err != nil { + return nil, err + } + // if running this function from cli_test directory do not include it in golang path + path = strings.TrimSuffix(path, "/cli_test") + return ioutil.ReadFile("/" + path + "/scripts/json_data/" + filename) } func attachFlagFileinput(cmd *cobra.Command) { diff --git a/golang/x/relay/gen_state.go b/golang/x/relay/gen_state.go index 60076f06..c72e641f 100644 --- a/golang/x/relay/gen_state.go +++ b/golang/x/relay/gen_state.go @@ -3,16 +3,26 @@ package relay import ( "encoding/json" "io/ioutil" + "os" + "strings" ) func getGenesisHeaders() (BitcoinHeader, []BitcoinHeader) { - headerJSON, jsonErr := ioutil.ReadFile("scripts/json_data/genesis.json") + // get path to root directory + path, err := os.Getwd() + if err != nil { + panic(err.Error()) + } + // if running this function from cli_test directory do not include it in golang path + path = strings.TrimSuffix(path, "/cli_test") + + headerJSON, jsonErr := ioutil.ReadFile("/" + path + "/scripts/json_data/genesis.json") if jsonErr != nil { panic("could not retreive data in gen_state: " + jsonErr.Error()) } var genesisHeaders []BitcoinHeader - err := json.Unmarshal([]byte(headerJSON), &genesisHeaders) + err = json.Unmarshal([]byte(headerJSON), &genesisHeaders) if err != nil { panic("bad json in gen_state: " + err.Error()) } diff --git a/golang/x/relay/types/querier.go b/golang/x/relay/types/querier.go index 9bd254e1..4b1d6a94 100644 --- a/golang/x/relay/types/querier.go +++ b/golang/x/relay/types/querier.go @@ -96,7 +96,6 @@ func (r QueryResGetBestDigest) String() string { return fmt.Sprintf("%s\n", digest) } - // QueryParamsFindAncestor represents the parameters for a FindAncestor query type QueryParamsFindAncestor struct { DigestLE Hash256Digest `json:"digestLE"` From 8ce42b7a813ae6aefe8f19e6ca5d8ba499b2c67d Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 13 Apr 2020 13:50:07 -0700 Subject: [PATCH 094/148] chore: add more information to READMEs --- golang/EXTENDING.md | 71 +++++++++++++++++++ golang/README.md | 162 ++++++++++++++++++++------------------------ 2 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 golang/EXTENDING.md diff --git a/golang/EXTENDING.md b/golang/EXTENDING.md new file mode 100644 index 00000000..e9669581 --- /dev/null +++ b/golang/EXTENDING.md @@ -0,0 +1,71 @@ +## Adding new functionality + +This is a cosmos-sdk module. It can be extended with new messages and/or +queries. Generally, this module is feature-complete, and should not be +extended. The main exception is the WIP hooks system on proof validation. All +other functionality should likely be put into a separate module. + +In order to extend this module, follow these steps: + +## How to add a view function (queries) +1. Add necessary getter(s) in `x/relay/keeper/keeper.go` +1. Add response type to `x/relay/types/querier.go` + 1. Add new string tag for the new query + 1. Response type is a struct with the return values + 1. Implement `String()` for the response type +1. Add function to querier `x/relay/keeper/querier.go` + 1. Add new `query___` function + 1. Add new case block to `switch` in `NewQuerier()` +1. Add to CLI + 1. add to `x/relay/client/cli/query.go` + 1. `func GetCmd______` + 1. returns a `cobra.Command` object + 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` + 1. `RunE` parses args, returns errors, and calls `cliCtx.QueryWithData` + 1. parses the output and returns it with `cliCtx.PrintOutput` +1. Add to REST + 1. add to `x/relay/client/rest/query.go` + 1. new function `_____Handler` + 1. parse args and build structs + 1. cliCtx.QueryWithData + 1. return errors with `rest.WriteErrorResponse` + 1. return query result with `rest.PostProcessResponse` + 1. add GET route to `x/relay/client/rest/rest.go` + 1. new `s.HandleFunc` with the route and arguments + 1. `.Methods("GET")` + 1. duplicate for optional args (see `isancestor` for example) + + +## How to add a non-view function (messages) +1. Add necessary getters/setters in `x/relay/keeper/keeper.go` +1. Add msg type in `x/relay/types/msgs.go` + 1. Message type is a struct with the arguments + 1. Implement `New___()` + 1. Implement `GetSigners()` <--- Ask me about this later + 1. Implement `Type()` + 1. Implement `ValidateBasic()` + 1. Implement `GetSignBytes()` + 1. Implement `Route()` +1. Add to handler + 1. Add new `handle____` function + 1. Add new case block to `switch` in `NewHandler()` +1. Add aliases in `x/relay/alias.go` + 1. Add alias in `var` block + 1. Add alias in `type` block +1. Add to CLI + 1. add to `x/relay/client/cli/tx.go` + 1. `func GetCmd______` + 1. returns a `cobra.Command` object + 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` + 1. `RunE` parses args, returns errors, and calls `utils.GenerateOrBroadcastMsgs` +1. Add to REST + 1. add to `x/relay/client/rest/tx.go` + 1. new http request struct `______Req` + 1. `BaseReq` + the struct from `x/relay/types/msgs.go` + 1. new function `_____Handler` + 1. parse args and build structs + 1. return errors with `rest.WriteErrorResponse` + 1. make the tx with `utils.WriteGenerateStdTxResponse` + 1. add POST route to `x/relay/client/rest/rest.go` + 1. new `s.HandleFunc` with the route and arguments + 1. `.Methods("POST")` diff --git a/golang/README.md b/golang/README.md index 18ead7c3..87d9d940 100644 --- a/golang/README.md +++ b/golang/README.md @@ -1,4 +1,36 @@ -## TODOs: +## cosmos-sdk Bitcoin Relay + +This is a full-featured Bitcoin relay module for cosmos-sdk chains. It indexes +Bitcoin headers, provides information about the latest-known state of the +Bitcoin chain, and validates SPV Proofs against its view of the chain. It is a +critical component for many Cosmos applications to interact with Bitcoin. + +For more information about the relay's architecture see the `README.md` in the +repo's root directory. + +## Building the daemon and cli + +```sh +$ make +# To additionally install them in your `$GOPATH/bin` directory: +$ make install +``` + +## Running tests + +Run the unit tests as follows: + +```sh +$ go test ./x/... +``` + +See the README in `./cli_test` for instructions on running the integration +tests. + +Instructions for setting up manual testing can be found in the README in +`./scripts`. + +## Project Status - [X] Milestone 1 - [X] Milestone 2 @@ -8,96 +40,46 @@ - - [X] Validate SPV Proofs - - [X] `ProvideProof` message - [ ] Milestone 3 -- - [ ] Provide tooling for manual testing (scripts, docs, json test files) -- - [ ] Integration Tests +- - [X] Provide tooling for manual testing (scripts, docs, json test files) +- - [X] Integration Tests - - [ ] Document relay design & architecture -- - [ ] Document public interface +- - [X] Document public interface - - [ ] Provide hooks to execute tasks + dispatch messages - - [ ] Add a basic web dashboard with Relay health -## Messages added to CLI - -| Message | Status | Description | -| ------- | ------ | ----------- | -| IngestHeaderChain | Completed | Add a chain of headers to the relay | -| IngestDifficultyChange | Completed | Add a chain of headers to the relay with a difficulty change| -| MarkNewHeaviest | Completed | Mark a new best-known chain tip | -| NewRequest | Completed | Register a new SPV Proof request| -| ProvideProof | Completed | Provide a proof that satisfies 1 or more requests | - -## Queries added to CLI - -| Query | Status | Description | -| ----- | ------ | ----------- | -| IsAncestor | Completed | Deteremine if a block is an ancestor of another | -| GetRelayGenesis | Completed | Get the trusted root of the relay | -| GetLastReorgLCA | Completed | Get the LCA of the latest reorg | -| FindAncestor | Completed | Find the nth ancestor of a block| -| IsMostRecentCommonAncestor | Completed | Determine if a block is the LCA of two headers| -| HeaviestFromAncestor | Completed | Check which of two descendents is heaviest from the LCA | -| GetRequest | Completed | Get details of an SPV Proof Request| -| CheckProof | Completed | Check the syntactic validity of an SPV Proof | -| CheckRequests | Completed | Perform CheckProof and check the SPV Proof against a set of Requests | - -## How to add a view function (queries) -1. Add necessary getter(s) in `x/relay/keeper/keeper.go` -1. Add response type to `x/relay/types/querier.go` - 1. Add new string tag for the new query - 1. Response type is a struct with the return values - 1. Implement `String()` for the response type -1. Add function to querier `x/relay/keeper/querier.go` - 1. Add new `query___` function - 1. Add new case block to `switch` in `NewQuerier()` -1. Add to CLI - 1. add to `x/relay/client/cli/query.go` - 1. `func GetCmd______` - 1. returns a `cobra.Command` object - 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` - 1. `RunE` parses args, returns errors, and calls `cliCtx.QueryWithData` - 1. parses the output and returns it with `cliCtx.PrintOutput` -1. Add to REST - 1. add to `x/relay/client/rest/query.go` - 1. new function `_____Handler` - 1. parse args and build structs - 1. cliCtx.QueryWithData - 1. return errors with `rest.WriteErrorResponse` - 1. return query result with `rest.PostProcessResponse` - 1. add GET route to `x/relay/client/rest/rest.go` - 1. new `s.HandleFunc` with the route and arguments - 1. `.Methods("GET")` - 1. duplicate for optional args (see `isancestor` for example) - - -## How to add a non-view function (messages) -1. Add necessary getters/setters in `x/relay/keeper/keeper.go` -1. Add msg type in `x/relay/types/msgs.go` - 1. Message type is a struct with the arguments - 1. Implement `New___()` - 1. Implement `GetSigners()` <--- Ask me about this later - 1. Implement `Type()` - 1. Implement `ValidateBasic()` - 1. Implement `GetSignBytes()` - 1. Implement `Route()` -1. Add to handler - 1. Add new `handle____` function - 1. Add new case block to `switch` in `NewHandler()` -1. Add aliases in `x/relay/alias.go` - 1. Add alias in `var` block - 1. Add alias in `type` block -1. Add to CLI - 1. add to `x/relay/client/cli/tx.go` - 1. `func GetCmd______` - 1. returns a `cobra.Command` object - 1. define `Use` `Example` `Short` `Long` `Args` and `RunE` - 1. `RunE` parses args, returns errors, and calls `utils.GenerateOrBroadcastMsgs` -1. Add to REST - 1. add to `x/relay/client/rest/tx.go` - 1. new http request struct `______Req` - 1. `BaseReq` + the struct from `x/relay/types/msgs.go` - 1. new function `_____Handler` - 1. parse args and build structs - 1. return errors with `rest.WriteErrorResponse` - 1. make the tx with `utils.WriteGenerateStdTxResponse` - 1. add POST route to `x/relay/client/rest/rest.go` - 1. new `s.HandleFunc` with the route and arguments - 1. `.Methods("POST")` + +## API + +Cosmos modules expose messages, which modify state, and queries, which read +state. + +### Queries + +Queries are available via CLI or REST. For more information, see the +descriptions in the CLI. + +| Query | Description | +| ----- | ----------- | +| IsAncestor | Deteremine if a block is an ancestor of another | +| GetRelayGenesis | Get the trusted root of the relay | +| GetLastReorgLCA | Get the LCA of the latest reorg | +| GetLastReorgLCA | Get the best digest known to the relay | +| FindAncestor | Find the nth ancestor of a block| +| IsMostRecentCommonAncestor | Determine if a block is the LCA of two headers| +| HeaviestFromAncestor | Check which of two descendents is heaviest from the LCA | +| GetRequest | Get details of an SPV Proof Request| +| CheckProof | Check the syntactic validity of an SPV Proof | +| CheckRequests | Perform CheckProof and check the SPV Proof against a set of Requests | + +### Messages + +Messages are available via CLI or REST. For more information, see the +descriptions in the CLI. + +| Message | Description | +| ------- | ----------- | +| IngestHeaderChain | Add a chain of headers to the relay | +| IngestDifficultyChange | Add a chain of headers to the relay with a difficulty change| +| MarkNewHeaviest | Mark a new best-known chain tip | +| NewRequest | Register a new SPV Proof request | +| ProvideProof | Provide a proof that satisfies 1 or more requests | From 8a837a29e3b8ff40eadfeec6072cc2e2dc3cd8d1 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 13 Apr 2020 13:50:57 -0700 Subject: [PATCH 095/148] chore: add note about golang + solidity relay --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7cc95423..4b80c57e 100644 --- a/README.md +++ b/README.md @@ -43,5 +43,8 @@ reducing calldata gas costs. ### Project Notes -The Python relay mainter in `./maintainer/` is not thoroughly tested, and does not -yet support the cosmos-sdk relay. +Complete relays are available in Solidity, for EVM-based chains (like Ethereum) +and Golang using the cosmos-sdk framework. + +The Python relay mainter in `./maintainer/` is not thoroughly tested, and does +not yet support the cosmos-sdk relay. From 4cf1570f8dc25c9a9cfe3fa0f2f3f0f1dba01d39 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Mon, 13 Apr 2020 13:58:27 -0700 Subject: [PATCH 096/148] chore: update READMEs --- golang/EXTENDING.md | 19 ++++++++++++++++++- golang/fake_app.go | 2 +- solidity/README.md | 10 ++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/golang/EXTENDING.md b/golang/EXTENDING.md index e9669581..f5b0eea5 100644 --- a/golang/EXTENDING.md +++ b/golang/EXTENDING.md @@ -5,7 +5,24 @@ queries. Generally, this module is feature-complete, and should not be extended. The main exception is the WIP hooks system on proof validation. All other functionality should likely be put into a separate module. -In order to extend this module, follow these steps: +### Integrating with other modules + +An instance of `relay.Keeper` should be added to your app in `app.go`. It can +be instantiated as follows: + +``` +app.relayKeeper = relay.NewKeeper( + keys[relay.StoreKey], + app.cdc, + true, // Mainnet // TODO: pass this in somehow +) +``` + +After that, the relay can be accessed via the Keeper's public interface. + +### Extending this module + +In order to extend this module, follow these steps: ## How to add a view function (queries) 1. Add necessary getter(s) in `x/relay/keeper/keeper.go` diff --git a/golang/fake_app.go b/golang/fake_app.go index 81cf7954..ae9212f4 100644 --- a/golang/fake_app.go +++ b/golang/fake_app.go @@ -195,7 +195,7 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], app.cdc, - true, // TODO: pass this in somehow + true, // Mainnet // TODO: pass this in somehow ) app.mm = module.NewManager( diff --git a/solidity/README.md b/solidity/README.md index ef1c216d..ffe645f1 100644 --- a/solidity/README.md +++ b/solidity/README.md @@ -1,8 +1,14 @@ +## cosmos-sdk Bitcoin Relay + +This is a full-featured Bitcoin relay module for EVM chains. It indexes +Bitcoin headers, provides information about the latest-known state of the +Bitcoin chain, and validates SPV Proofs against its view of the chain. It is a +critical component for many EVM applications to interact with Bitcoin. + ### How do I develop for it? ``` $ npm i -g truffle $ npm i -$ truffle compile -$ npm run lint +$ npm run test ``` From 7a5e48cb62b8dbcdfc2c778c8378af8087aed6e5 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Mon, 13 Apr 2020 20:20:22 -0400 Subject: [PATCH 097/148] Travis-CI Integration (#64) * Lint Solidity files * Travis-CI Integration --- .travis.yml | 24 ++++++++++++++++++++++++ solidity/contracts/Migrations.sol | 30 +++++++++++++++--------------- solidity/contracts/OnDemandSPV.sol | 20 ++++++++++---------- solidity/contracts/Relay.sol | 8 ++++---- solidity/test/Relay.test.js | 18 +++++++++--------- solidity/test/utils.js | 2 +- 6 files changed, 63 insertions(+), 39 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..3748ab35 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +matrix: + include: + - language: go + go: 1.13.1 + env: + - GO111MODULE=on + before_script: + - cd golang + - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.20.0 + - make + script: + - go test ./x/relay/keeper + - go test ./x/relay/types + - go test -mod=readonly `go list ./cli_test/...` + + - language: node_js + node_js: 10.14.2 + before_script: + - cd solidity + - npm install -g truffle + - npm install + - npm run lint + script: + - npm run test diff --git a/solidity/contracts/Migrations.sol b/solidity/contracts/Migrations.sol index c378ffb0..89d6ee33 100644 --- a/solidity/contracts/Migrations.sol +++ b/solidity/contracts/Migrations.sol @@ -1,23 +1,23 @@ pragma solidity >=0.4.21 <0.6.0; contract Migrations { - address public owner; - uint public last_completed_migration; + address public owner; + uint public last_completed_migration; - constructor() public { - owner = msg.sender; - } + constructor() public { + owner = msg.sender; + } - modifier restricted() { - if (msg.sender == owner) _; - } + modifier restricted() { + if (msg.sender == owner) _; + } - function setCompleted(uint completed) public restricted { - last_completed_migration = completed; - } + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } - function upgrade(address new_address) public restricted { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); - } + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } } diff --git a/solidity/contracts/OnDemandSPV.sol b/solidity/contracts/OnDemandSPV.sol index ba10b694..64afb37f 100644 --- a/solidity/contracts/OnDemandSPV.sol +++ b/solidity/contracts/OnDemandSPV.sol @@ -139,36 +139,36 @@ contract OnDemandSPV is ISPVRequestManager, Relay { ) internal returns (uint256) { uint256 _requestID = nextID; nextID = nextID + 1; + bytes memory pays = _pays; - uint256 _spendsLen = _spends.length; - require(_spendsLen == 36 || _spendsLen == 0, "Not a valid UTXO"); + require(_spends.length == 36 || _spends.length == 0, "Not a valid UTXO"); /* NB: This will fail if the output is not p2pkh, p2sh, p2wpkh, or p2wsh*/ - uint256 _paysLen = _pays.length; + uint256 _paysLen = pays.length; // if it's not length-prefixed, length-prefix it - if (_paysLen > 0 && uint8(_pays[0]) != _paysLen - 1) { - _pays = abi.encodePacked(uint8(_paysLen), _pays); + if (_paysLen > 0 && uint8(pays[0]) != _paysLen - 1) { + pays = abi.encodePacked(uint8(_paysLen), pays); _paysLen += 1; // update the length because we made it longer } - bytes memory _p = abi.encodePacked(bytes8(0), _pays); + bytes memory _p = abi.encodePacked(bytes8(0), pays); require( _paysLen == 0 || // no request OR _p.extractHash().length > 0 || // standard output OR _p.extractOpReturnData().length > 0, // OP_RETURN output "Not a standard output type"); - require(_spendsLen > 0 || _paysLen > 0, "No request specified"); + require(_spends.length > 0 || _paysLen > 0, "No request specified"); ProofRequest storage _req = requests[_requestID]; _req.owner = msg.sender; - if (_spendsLen > 0) { + if (_spends.length > 0) { _req.spends = keccak256(_spends); } if (_paysLen > 0) { - _req.pays = keccak256(_pays); + _req.pays = keccak256(pays); } if (_paysValue > 0) { _req.paysValue = _paysValue; @@ -182,7 +182,7 @@ contract OnDemandSPV is ISPVRequestManager, Relay { _req.consumer = _consumer; _req.state = RequestStates.ACTIVE; - emit NewProofRequest(msg.sender, _requestID, _paysValue, _spends, _pays); + emit NewProofRequest(msg.sender, _requestID, _paysValue, _spends, pays); return _requestID; } diff --git a/solidity/contracts/Relay.sol b/solidity/contracts/Relay.sol index 72f86360..124816d5 100644 --- a/solidity/contracts/Relay.sol +++ b/solidity/contracts/Relay.sol @@ -225,7 +225,7 @@ contract Relay is IRelay { // Don't update if this is a deep past epoch uint256 _oldDiff = _oldPeriodStartHeader.extractDifficulty(); if (prevEpochDiff != _oldDiff && _endHeight > _findHeight(bestKnownDigest).sub(2016)) { - prevEpochDiff = _oldDiff; + prevEpochDiff = _oldDiff; } // Pass all but the first through to be added @@ -338,7 +338,7 @@ contract Relay is IRelay { uint256 _newDiff = _newBest.extractDifficulty(); if (_newDiff != currentEpochDiff) { - currentEpochDiff = _newDiff; + currentEpochDiff = _newDiff; } emit Reorg( @@ -481,13 +481,13 @@ contract Relay is IRelay { /// @dev This is updated when a new heavist header has a new diff /// @return The difficulty of the bestKnownDigest function getCurrentEpochDifficulty() external view returns (uint256) { - return currentEpochDiff; + return currentEpochDiff; } /// @notice Getter for prevEpochDiff /// @dev This is updated when a difficulty change is accepted /// @return The difficulty of the previous epoch function getPrevEpochDifficulty() external view returns (uint256) { - return prevEpochDiff; + return prevEpochDiff; } /// @notice Getter for relayGenesis diff --git a/solidity/test/Relay.test.js b/solidity/test/Relay.test.js index 5be0fa5a..6e1d3bd9 100644 --- a/solidity/test/Relay.test.js +++ b/solidity/test/Relay.test.js @@ -474,9 +474,9 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const pre = utils.concatenateHeadersHexes(PRE_CHAIN) - const post = utils.concatenateHeadersHexes(POST_CHAIN) - const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const pre = utils.concatenateHeadersHexes(PRE_CHAIN); + const post = utils.concatenateHeadersHexes(POST_CHAIN); + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)); const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { @@ -539,9 +539,9 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const pre = utils.concatenateHeadersHexes(PRE_CHAIN) - const post = utils.concatenateHeadersHexes(POST_CHAIN) - const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const pre = utils.concatenateHeadersHexes(PRE_CHAIN); + const post = utils.concatenateHeadersHexes(POST_CHAIN); + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)); const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { @@ -620,9 +620,9 @@ contract('Relay', async () => { const POST_CHAIN = REORG_AND_RETARGET_CHAIN.postRetargetChain; const orphan = REORG_AND_RETARGET_CHAIN.orphan_437478; - const pre = utils.concatenateHeadersHexes(PRE_CHAIN) - const post = utils.concatenateHeadersHexes(POST_CHAIN) - const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)) + const pre = utils.concatenateHeadersHexes(PRE_CHAIN); + const post = utils.concatenateHeadersHexes(POST_CHAIN); + const shortPost = utils.concatenateHeadersHexes(POST_CHAIN.slice(0, POST_CHAIN.length - 2)); const postWithOrphan = utils.concatenateHexStrings([shortPost, orphan.hex]); before(async () => { diff --git a/solidity/test/utils.js b/solidity/test/utils.js index 571ea5ac..71c26af0 100644 --- a/solidity/test/utils.js +++ b/solidity/test/utils.js @@ -13,7 +13,7 @@ module.exports = { return current; }, concatenateHeadersHexes: function concatenateHeadersHexes(arr) { - const hexes = arr.map(arr => arr.hex); + const hexes = arr.map(_arr => _arr.hex); return this.concatenateHexStrings(hexes); } }; From f44331a23891e7093ac6b977aaa3ca9ed984fc54 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Mon, 13 Apr 2020 17:20:39 -0700 Subject: [PATCH 098/148] dep: bump soliditycoverage to use truffle plugin (#67) --- solidity/migrations/2_deploy_contracts.js | 2 +- solidity/package-lock.json | 198 +++++----------------- solidity/package.json | 4 +- solidity/truffle-config.js | 1 + 4 files changed, 44 insertions(+), 161 deletions(-) diff --git a/solidity/migrations/2_deploy_contracts.js b/solidity/migrations/2_deploy_contracts.js index 338dad0b..4b7e20b8 100644 --- a/solidity/migrations/2_deploy_contracts.js +++ b/solidity/migrations/2_deploy_contracts.js @@ -10,7 +10,7 @@ function sleep(milliseconds) { } module.exports = async (deployer, network) => { - if (['test', 'development', 'coverage'].includes(network)) { + if (['test', 'development', 'soliditycoverage'].includes(network)) { // never run deployments on development. We deploy in tests return; } diff --git a/solidity/package-lock.json b/solidity/package-lock.json index 3b27a3bd..25a95213 100644 --- a/solidity/package-lock.json +++ b/solidity/package-lock.json @@ -751,6 +751,12 @@ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" }, + "@solidity-parser/parser": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.5.2.tgz", + "integrity": "sha512-uRyvnvVYmgNmTBpWDbBsH/0kPESQhQpEc4KsvMRLVzFJ1o1s0uIv0Y6Y9IB5vI1Dwz2CbS4X/y4Wyw/75cTFnQ==", + "dev": true + }, "@stablelib/binary": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-0.7.2.tgz", @@ -826,9 +832,9 @@ }, "dependencies": { "@types/node": { - "version": "12.12.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz", - "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==", + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", "dev": true }, "bn.js": { @@ -866,9 +872,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.18.tgz", - "integrity": "sha512-DQ2hl/Jl3g33KuAUOcMrcAOtsbzb+y/ufakzAdeK9z/H/xsvkpbETZZbPNMIiQuk24f5ZRMCcZIViAwyFIiKmg==", + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==", "dev": true } } @@ -978,9 +984,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.18.tgz", - "integrity": "sha512-DQ2hl/Jl3g33KuAUOcMrcAOtsbzb+y/ufakzAdeK9z/H/xsvkpbETZZbPNMIiQuk24f5ZRMCcZIViAwyFIiKmg==", + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==", "dev": true }, "ethers": { @@ -5893,139 +5899,23 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" }, "handlebars": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.5.tgz", - "integrity": "sha512-PiM2ZRLZ0X+CIRSX66u7tkQi3rzrlSHAuioMBI1XP8DsfDaXEA+sD7Iyyoz4QACFuhX5z+IimN+n3BFWvvgWrQ==", + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", "dev": true, "requires": { + "minimist": "^1.2.5", "neo-async": "^2.6.0", "source-map": "^0.6.1", "uglify-js": "^3.1.4", - "yargs": "^14.2.3" + "wordwrap": "^1.0.0" }, "dependencies": { - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", - "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "decamelize": "^1.2.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^15.0.1" - } - }, - "yargs-parser": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", - "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -8901,9 +8791,9 @@ "dev": true }, "mkdirp": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", - "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { "minimist": "^1.2.5" @@ -9373,11 +9263,12 @@ } }, "solidity-coverage": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.7.2.tgz", - "integrity": "sha512-LizgpcrkMRmZdvOmwYN7l/h9ak0Yq/5BuXjEaH0Phds1Gu5hZfHGyvplgWYDFmzLwGB7hZ6d0/IdkdYm4HK0mg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.7.4.tgz", + "integrity": "sha512-eaYjKLJK7n+uAN9Xrm2kDujXOkxxBbHfNrjK+ZabU1DR4QWSVQiwDDF2JLciN943kkZdd2JOzJGp32NxCbdCWA==", "dev": true, "requires": { + "@solidity-parser/parser": "^0.5.2", "@truffle/provider": "^0.1.17", "chalk": "^2.4.2", "death": "^1.1.0", @@ -9394,14 +9285,13 @@ "recursive-readdir": "^2.2.2", "sc-istanbul": "^0.4.5", "shelljs": "^0.8.3", - "solidity-parser-diligence": "^0.4.16", "web3": "1.2.6" }, "dependencies": { "@types/node": { - "version": "12.12.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz", - "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==", + "version": "12.12.35", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.35.tgz", + "integrity": "sha512-ASYsaKecA7TUsDrqIGPNk3JeEox0z/0XR/WsJJ8BIX/9+SkMSImQXKWfU/yBrSyc7ZSE/NPqLu36Nur0miCFfQ==", "dev": true }, "bn.js": { @@ -9429,9 +9319,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.18.tgz", - "integrity": "sha512-DQ2hl/Jl3g33KuAUOcMrcAOtsbzb+y/ufakzAdeK9z/H/xsvkpbETZZbPNMIiQuk24f5ZRMCcZIViAwyFIiKmg==", + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==", "dev": true } } @@ -9482,9 +9372,9 @@ }, "dependencies": { "@types/node": { - "version": "10.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.18.tgz", - "integrity": "sha512-DQ2hl/Jl3g33KuAUOcMrcAOtsbzb+y/ufakzAdeK9z/H/xsvkpbETZZbPNMIiQuk24f5ZRMCcZIViAwyFIiKmg==", + "version": "10.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.19.tgz", + "integrity": "sha512-46/xThm3zvvc9t9/7M3AaLEqtOpqlYYYcCZbpYVAQHG20+oMZBkae/VMrn4BTi6AJ8cpack0mEXhGiKmDNbLrQ==", "dev": true } } @@ -10820,14 +10710,13 @@ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" }, "uglify-js": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", - "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.0.tgz", + "integrity": "sha512-j5wNQBWaql8gr06dOUrfaohHlscboQZ9B8sNsoK5o4sBjm7Ht9dxSbrMXyktQpA16Acaij8AcoozteaPYZON0g==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" + "commander": "~2.20.3" }, "dependencies": { "commander": { @@ -10836,13 +10725,6 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "optional": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true } } }, diff --git a/solidity/package.json b/solidity/package.json index 42b97b8b..ab57522a 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "compile": "truffle compile", - "test:coverage": "npx solidity-coverage", + "test:coverage": "truffle run coverage", "lint": "solium -d contracts/ && eslint ./test", "lint:fix": "solium --fix -d contracts/ && eslint --fix ./test", "test": "truffle test" @@ -23,7 +23,7 @@ "eslint-config-airbnb-base": "^13.2.0", "eslint-plugin-import": "^2.20.2", "eth-gas-reporter": "^0.2.16", - "solidity-coverage": "^0.7.2", + "solidity-coverage": "^0.7.4", "solium": "^1.2.4", "truffle-hdwallet-provider": "^1.0.17", "truffle-plugin-verify": "^0.3.10" diff --git a/solidity/truffle-config.js b/solidity/truffle-config.js index 9d502588..813c3c98 100644 --- a/solidity/truffle-config.js +++ b/solidity/truffle-config.js @@ -47,6 +47,7 @@ module.exports = { etherscan: process.env.ETHERSCAN_KEY }, plugins: [ + 'solidity-coverage', 'truffle-plugin-verify' ], networks: { From d60b9c244a913a0cbf3ae003288aec0b17d94f79 Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Mon, 13 Apr 2020 17:20:52 -0700 Subject: [PATCH 099/148] Tests for OnDemandSPV (#69) * dep: bump soliditycoverage to use truffle plugin * tests: port tests from private repo --- solidity/contracts/OnDemandSPV.sol | 2 - solidity/contracts/test/DummyOnDemandSPV.sol | 132 +++++ solidity/test/OnDemandSPV.test.js | 582 +++++++++++++++++++ solidity/test/OnDemandSPVHelpers.json | 13 + 4 files changed, 727 insertions(+), 2 deletions(-) create mode 100644 solidity/contracts/test/DummyOnDemandSPV.sol create mode 100644 solidity/test/OnDemandSPV.test.js create mode 100644 solidity/test/OnDemandSPVHelpers.json diff --git a/solidity/contracts/OnDemandSPV.sol b/solidity/contracts/OnDemandSPV.sol index 64afb37f..98e13480 100644 --- a/solidity/contracts/OnDemandSPV.sol +++ b/solidity/contracts/OnDemandSPV.sol @@ -54,8 +54,6 @@ contract OnDemandSPV is ISPVRequestManager, Relay { nextID = _firstID; } - function () external payable {} - /// @notice Cancel a bitcoin event request. /// @dev Prevents the relay from forwarding tx infromation /// @param _requestID The ID of the request to be cancelled diff --git a/solidity/contracts/test/DummyOnDemandSPV.sol b/solidity/contracts/test/DummyOnDemandSPV.sol new file mode 100644 index 00000000..1608d8a2 --- /dev/null +++ b/solidity/contracts/test/DummyOnDemandSPV.sol @@ -0,0 +1,132 @@ +pragma solidity ^0.5.10; + +/** @title OnDemandSPV */ +/** @author Summa (https://summa.one) */ + +import {ISPVConsumer} from "../Interfaces.sol"; +import {OnDemandSPV} from "../OnDemandSPV.sol"; + +contract DummyConsumer is ISPVConsumer { + event Consumed(bytes32 indexed _txid, uint256 indexed _requestID, uint256 _gasLeft); + + bool broken = false; + + function setBroken(bool _b) external { + broken = _b; + } + + function spv( + bytes32 _txid, + bytes calldata, + bytes calldata, + uint256 _requestID, + uint8, + uint8 + ) external { + emit Consumed(_txid, _requestID, gasleft()); + if (broken) { + revert('BORKED'); + } + } + + function cancel( + uint256 _requestID, + address payable _odspv + ) external returns (bool) { + return OnDemandSPV(_odspv).cancelRequest(_requestID); + } +} + +contract DummyOnDemandSPV is OnDemandSPV { + + constructor( + bytes memory _genesisHeader, + uint256 _height, + bytes32 _periodStart, + uint256 _firstID + ) OnDemandSPV( + _genesisHeader, + _height, + _periodStart, + _firstID + ) public {return ;} + + bool callResult = false; + + function requestTest( + uint256 _requestID, + bytes calldata _spends, + bytes calldata _pays, + uint64 _paysValue, + address _consumer, + uint8 _numConfs, + uint256 _notBefore + ) external returns (uint256) { + nextID = _requestID; + return _request(_spends, _pays, _paysValue, _consumer, _numConfs, _notBefore); + } + + function setCallResult(bool _r) external { + callResult = _r; + } + + function _isAncestor(bytes32, bytes32, uint256) internal view returns (bool) { + return callResult; + } + + function getValidatedTx(bytes32 _txid) public view returns (bool) { + return validatedTxns[_txid]; + } + + function setValidatedTx(bytes32 _txid) public { + validatedTxns[_txid] = true; + } + + function unsetValidatedTx(bytes32 _txid) public { + validatedTxns[_txid] = false; + } + + function callCallback( + bytes32 _txid, + uint16 _reqIndices, + bytes calldata _vin, + bytes calldata _vout, + uint256 _requestID + ) external returns (bool) { + return _callCallback(_txid, _reqIndices, _vin, _vout, _requestID); + } + + function checkInclusion( + bytes calldata _header, + bytes calldata _proof, + uint256 _index, + bytes32 _txid, + uint256 _requestID + ) external view returns (bool) { + return _checkInclusion(_header, _proof, _index, _txid, _requestID); + } + + function _getConfs(bytes32 _header) internal view returns (uint8){ + if (_header == bytes32(0)) { + return OnDemandSPV._getConfs(lastReorgCommonAncestor); + } + return 8; + } + + function getConfsTest() external view returns (uint8) { + return _getConfs(bytes32(0)); + } + + function checkRequests( + uint16 _requestIndices, + bytes calldata _vin, + bytes calldata _vout, + uint256 _requestID + ) external view returns (bool) { + return _checkRequests(_requestIndices, _vin, _vout, _requestID); + } + + function whatTimeIsItRightNowDotCom() external view returns (uint256) { + return block.timestamp; + } +} diff --git a/solidity/test/OnDemandSPV.test.js b/solidity/test/OnDemandSPV.test.js new file mode 100644 index 00000000..9d6b0589 --- /dev/null +++ b/solidity/test/OnDemandSPV.test.js @@ -0,0 +1,582 @@ +/* global artifacts contract before after describe it assert web3 */ +const BN = require('bn.js'); +const constants = require('./OnDemandSPVHelpers.json'); +const REGULAR_CHAIN = require('./headers.json'); + +const DummyConsumer = artifacts.require('DummyConsumer'); +const DummyOnDemandSPV = artifacts.require('DummyOnDemandSPV'); + +contract('OnDemandSPV', async (accounts) => { + let instance; + let consumer; + + const [deployer, requestOwner, outsideCaller] = accounts; + const { genesis } = REGULAR_CHAIN; + const BYTES32_0 = '0x0000000000000000000000000000000000000000000000000000000000000000'; + + before(async () => { + instance = await DummyOnDemandSPV.new( + genesis.hex, + genesis.height, + genesis.digest_le, + 0, + { from: deployer } + ); + consumer = await DummyConsumer.new({ from: deployer }); + }); + + describe('#cancelRequest', async () => { + const [sub1Id, sub2Id] = [1, 2]; + + before(async () => { + await instance.requestTest( + sub1Id, + `0x${'00'.repeat(36)}`, + '0x', + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.requestTest( + sub2Id, + `0x${'00'.repeat(36)}`, + '0x', + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + }); + + it('errors if not active', async () => { + try { + await instance.cancelRequest(3); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Request not active'); + } + }); + + it('cannot be cancelled by an outside caller', async () => { + try { + await instance.cancelRequest(sub1Id, { from: outsideCaller }); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Can only be cancelled by owner or consumer'); + } + }); + + it('can be canceled by the owner', async () => { + await instance.cancelRequest(sub1Id, { from: requestOwner }); + const request = await instance.getRequest(sub1Id); + assert(request[3].eq(new BN('2', 10))); + }); + + it('can be canceled by the consumer', async () => { + await consumer.cancel(sub2Id, instance.address); + const request = await instance.getRequest(sub2Id); + assert(request[3].eq(new BN('2', 10))); + }); + }); + + describe('#getRequest', async () => { + const sub3Id = 3; + + before(async () => { + await instance.requestTest( + sub3Id, + `0x${'11'.repeat(36)}`, + '0x', + 100, + consumer.address, + 0, + 0, + { from: requestOwner } + ); + }); + + it('retrieves request information', async () => { + const res = await instance.getRequest(sub3Id); + + // this is the keccak256 of `0x${'11'.repeat(36)}` + assert.strictEqual(res[0], '0x600e7bfdb8c3cc85df9cd058022100f260c17e7c58603758d2c1ac92c63469a6'); + assert.strictEqual(res[1], BYTES32_0); + assert(res[2].eq(new BN('100', 10))); + assert(res[3].eq(new BN('1', 10))); + assert.strictEqual(res[4], consumer.address); + assert.strictEqual(res[5], requestOwner); + }); + }); + + describe('#request', async () => { + const sub4Id = 4; + + it('errors if the outpoint is present and not 36 bytes', async () => { + try { + await instance.requestTest(sub4Id, '0xff', '0x', 0, consumer.address, 1, 0, { from: requestOwner }); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Not a valid UTXO'); + } + }); + + it('errors if the output is present, but nonstandard', async () => { + try { + await instance.requestTest(sub4Id, '0x', '0x00000000000000000000000000', 0, consumer.address, 1, 0, { from: requestOwner }); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Not a standard output type'); + } + }); + + it('errors if spends and pays are both 0', async () => { + try { + await instance.requestTest(sub4Id, '0x', '0x', 0, consumer.address, 1, 0, { from: requestOwner }); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'No request specified'); + } + }); + + it('stores a request and emits an event', async () => { + const blockNumber = await web3.eth.getBlock('latest').number; + + await instance.requestTest(sub4Id, `0x${'11'.repeat(36)}`, '0x', 100, consumer.address, 1, 0, { from: requestOwner }); + const res = await instance.getRequest(sub4Id); + + // this is the keccak256 of `0x${'11'.repeat(36)}` + assert.strictEqual(res[0], '0x600e7bfdb8c3cc85df9cd058022100f260c17e7c58603758d2c1ac92c63469a6'); + assert.strictEqual(res[1], BYTES32_0); + assert(res[2].eq(new BN('100', 10))); + assert(res[3].eq(new BN('1', 10))); + assert.strictEqual(res[4], consumer.address); + assert.strictEqual(res[5], requestOwner); + + + const eventList = await instance.getPastEvents( + 'NewProofRequest', + { fromBlock: blockNumber, toBlock: 'latest' } + ); + /* eslint-disable-next-line no-underscore-dangle */ + assert.strictEqual(eventList[0].returnValues._requester, requestOwner); + /* eslint-disable-next-line no-underscore-dangle */ + assert.strictEqual(parseInt(eventList[0].returnValues._requestID, 10), 4); + }); + + it('stores bytes32(0) for unspecified spends', async () => { + await instance.requestTest( + 88, + '0x', + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + const res = await instance.getRequest(88); + assert.strictEqual(res[0], BYTES32_0); + }); + }); + + describe('#provideProof', async () => { + const sub5Id = 5; + + before(async () => { + await instance.requestTest( + sub5Id, + constants.OP_RETURN_SPENDS_0, + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.setCallResult(true); + }); + + after(async () => { + await instance.setCallResult(false); + }); + + it('runs succesfully, and sets validatedTxns and latestValidatedTx', async () => { + assert.isOk(await instance.provideProof( + constants.OP_RETURN_HEADER, + constants.OP_RETURN_PROOF, + constants.OP_RETURN_VERSION, + constants.OP_RETURN_LOCKTIME, + constants.OP_RETURN_INDEX, + '0x0001', // requestIndices + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub5Id + )); + + let res = await instance.getValidatedTx(constants.OP_RETURN_TX_ID_LE); + assert.isTrue(res); + + res = await instance.getLatestValidatedTx.call(); + assert.equal(res, constants.OP_RETURN_TX_ID_LE); + }); + + it('shortcuts inclusion validatins for already-seen txns', async () => { + if (!await instance.getValidatedTx.call(constants.OP_RETURN_TX_ID_LE)) { + await instance.setValidatedTx(constants.OP_RETURN_TX_ID_LE); + } + assert.isOk(await instance.provideProof( + '0x', + '0x', + constants.OP_RETURN_VERSION, + constants.OP_RETURN_LOCKTIME, + 0, + '0x0001', // requestIndices + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub5Id + )); + }); + }); + + describe('#callCallback', async () => { + const sub6Id = 6; + + async function submitValidProof() { + return instance.provideProof( + constants.OP_RETURN_HEADER, + constants.OP_RETURN_PROOF, + constants.OP_RETURN_VERSION, + constants.OP_RETURN_LOCKTIME, + constants.OP_RETURN_INDEX, + '0x0001', // requestIndices + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub6Id, + { gas: 4000000 } + ); + } + + before(async () => { + await instance.requestTest( + sub6Id, + constants.OP_RETURN_SPENDS_0, + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.setCallResult(true); + }); + + it('calls the consumer with 500,000 gas', async () => { + const blockNumber = await web3.eth.getBlock('latest').number; + + await submitValidProof(); + + const eventList = await consumer.getPastEvents( + 'Consumed', + { fromBlock: blockNumber, toBlock: 'latest' } + ); + /* eslint-disable-next-line no-underscore-dangle */ + assert(new BN(eventList[0].returnValues._gasLeft, 10).ltn(500000)); + }); + + it('functions even if the remote contract reverts', async () => { + const blockNumber = await web3.eth.getBlock('latest').number; + + await consumer.setBroken(true); + assert.isOk(await submitValidProof()); // doesn't revert + await consumer.setBroken(false); + + // should be no events, because consumer reverted + const eventList = await consumer.getPastEvents( + 'Consumed', + { fromBlock: blockNumber, toBlock: 'latest' } + ); + assert.strictEqual(eventList.length, 0); + }); + }); + + describe('#checkInclusion', async () => { + it('errors on a bad inclusion proof', async () => { + try { + await instance.checkInclusion( + constants.OP_RETURN_HEADER, + '0x', + constants.OP_RETURN_INDEX, + constants.OP_RETURN_TX_ID_LE, + 1, + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Bad inclusion proof'); + } + }); + + it('errors if isAncestor fails', async () => { + await instance.setCallResult(false); + try { + await instance.checkInclusion( + constants.OP_RETURN_HEADER, + constants.OP_RETURN_PROOF, + constants.OP_RETURN_INDEX, + constants.OP_RETURN_TX_ID_LE, + 1 + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'GCD does not confirm header'); + } + await instance.setCallResult(true); + }); + + it('errors if insufficinet confirmations', async () => { + const sub = 838; + await instance.requestTest( + sub, + `0x${'00'.repeat(36)}`, + '0x', + 0, + consumer.address, + 240, // very large conf requirement + 0, + { from: requestOwner } + ); + try { + await instance.checkInclusion( + constants.OP_RETURN_HEADER, + constants.OP_RETURN_PROOF, + constants.OP_RETURN_INDEX, + constants.OP_RETURN_TX_ID_LE, + sub + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Insufficient confirmations'); + } + await instance.setCallResult(true); + }); + + it('succeeds', async () => { + await instance.setCallResult(true); + const res = await instance.checkInclusion.call( + constants.OP_RETURN_HEADER, + constants.OP_RETURN_PROOF, + constants.OP_RETURN_INDEX, + constants.OP_RETURN_TX_ID_LE, + 1 + ); + assert.isTrue(res); + }); + }); + + describe('#checkRequests', async () => { + const sub7Id = 7; + const sub8Id = 8; + const sub9Id = 9; + + before(async () => { + await instance.requestTest( // both + sub7Id, + constants.OP_RETURN_SPENDS_0, + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.requestTest( // only spends + sub8Id, + constants.OP_RETURN_SPENDS_0, + '0x', + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.requestTest( // only pays + sub9Id, + '0x', + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + await instance.setCallResult(true); + }); + + it('errors if the vin is malformatted', async () => { + try { + await instance.checkRequests( + '0x0001', + `0x${'01'.repeat(66)}`, + constants.OP_RETURN_VOUT, + sub7Id + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Vin is malformatted'); + } + }); + + it('errors if the vout is malformatted', async () => { + try { + await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + `0x${'01'.repeat(66)}`, + sub7Id + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Vout is malformatted'); + } + }); + + it('errors if the request is not active', async () => { + try { + await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + 11 // fragile: not yet active. breaks if we add more cases above this; + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Request is not active'); + } + }); + + it('errors if the specified output does not match the pays request', async () => { + try { + await instance.checkRequests( + '0x0000', // first output instead of second + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub7Id + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Does not match pays request'); + } + + try { + await instance.checkRequests( + '0xFF00', // first output instead of second + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub9Id + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Does not match pays request'); + } + }); + + it('errors if the specified output does not match the paysValue request', async () => { + await instance.requestTest( + 333, + constants.OP_RETURN_SPENDS_0, + constants.OP_RETURN_PAYS_1, + 1000, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + try { + await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + 333 + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Does not match value request'); + } + }); + + it('errors if the specified input does not match the spends request', async () => { + await instance.requestTest( + 444, + `0x${'33'.repeat(36)}`, + constants.OP_RETURN_PAYS_1, + 0, + consumer.address, + 1, + 0, + { from: requestOwner } + ); + try { + await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + 444 + ); + assert(false, 'expected an error'); + } catch (e) { + assert.include(e.message, 'Does not match spends request'); + } + }); + + it('suceeds', async () => { + assert.ok(await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub7Id + )); + assert.ok(await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub8Id + )); + assert.ok(await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub9Id + )); + }); + + it('allows 0xFF for unchecked', async () => { + assert.ok(await instance.checkRequests( + '0x0001', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub7Id + )); + assert.ok(await instance.checkRequests( + '0x00FF', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub8Id + )); + assert.ok(await instance.checkRequests( + '0xFF01', + constants.OP_RETURN_VIN, + constants.OP_RETURN_VOUT, + sub9Id + )); + }); + }); + + describe('#_getConfs', async () => { + it('should return the number of confirmations', async () => { + const res = await instance.getConfsTest.call(); + assert(res.eq(new BN('0', 10))); // <- fragile: best and LCA are the same here + }); + }); +}); diff --git a/solidity/test/OnDemandSPVHelpers.json b/solidity/test/OnDemandSPVHelpers.json new file mode 100644 index 00000000..99a7d030 --- /dev/null +++ b/solidity/test/OnDemandSPVHelpers.json @@ -0,0 +1,13 @@ +{ + "OP_RETURN_TX": "0x010000000001011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffffff024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211024730440220276e0ec78028582054d86614c65bc4bf85ff5710b9d3a248ca28dd311eb2fa6802202ec950dd2a8c9435ff2d400cc45d7a4854ae085f49e05cc3f503834546d410de012103732783eef3af7e04d3af444430a629b16a9261e4025f52bf4d6d026299c37c7400000000", + "OP_RETURN_PROOF": "0xe35a0d6de94b656694589964a252957e4673a9fb1d2f8b4a92e3f0a7bb654fddb94e5a1e6d7f7f499fd1be5dd30a73bf5584bf137da5fdd77cc21aeb95b9e35788894be019284bd4fbed6dd6118ac2cb6d26bc4be4e423f55a3a48f2874d8d02a65d9c87d07de21d4dfe7b0a9f4a23cc9a58373e9e6931fefdb5afade5df54c91104048df1ee999240617984e18b6f931e2373673d0195b8c6987d7ff7650d5ce53bcec46e13ab4f2da1146a7fc621ee672f62bc22742486392d75e55e67b09960c3386a0b49e75f1723d6ab28ac9a2028a0c72866e2111d79d4817b88e17c821937847768d92837bae3832bb8e5a4ab4434b97e00a6c10182f211f592409068d6f5652400d9a3d1cc150a7fb692e874cc42d76bdafc842f2fe0f835a7c24d2d60c109b187d64571efbaa8047be85821f8e67e0e85f2f5894bc63d00c2ed9d64", + "OP_RETURN_INDEX": 281, + "OP_RETURN_VERSION": "0x01000000", + "OP_RETURN_VIN": "0x011746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba300000000000ffffffff", + "OP_RETURN_VOUT": "0x024897070000000000220020a4333e5612ab1a1043b25755c89b16d55184a42f81799e623e6bc39db8539c180000000000000000166a14edb1b5c2f39af0fec151732585b1049b07895211", + "OP_RETURN_LOCKTIME": "0x00000000", + "OP_RETURN_HEADER": "0x0000002073bd2184edd9c4fc76642ea6754ee40136970efc10c4190000000000000000000296ef123ea96da5cf695f22bf7d94be87d49db1ad7ac371ac43c4da4161c8c216349c5ba11928170d38782b", + "OP_RETURN_SPENDS_0": "0x1746bd867400f3494b8f44c24b83e1aa58c4f0ff25b4a61cffeffd4bc0f9ba3000000000", + "OP_RETURN_PAYS_1": "0x166a14edb1b5c2f39af0fec151732585b1049b07895211", + "OP_RETURN_TX_ID_LE": "0x48e5a1a0e616d8fd92b4ef228c424e0c816799a256c6a90892195ccfc53300d6" +} From 6231b487369b7e93974a7057bcb545401eb25abf Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Tue, 14 Apr 2020 11:44:57 -0700 Subject: [PATCH 100/148] lint: fix solidity indentation in test contract (#71) --- solidity/contracts/test/DummyOnDemandSPV.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solidity/contracts/test/DummyOnDemandSPV.sol b/solidity/contracts/test/DummyOnDemandSPV.sol index 1608d8a2..64f604c9 100644 --- a/solidity/contracts/test/DummyOnDemandSPV.sol +++ b/solidity/contracts/test/DummyOnDemandSPV.sol @@ -25,7 +25,7 @@ contract DummyConsumer is ISPVConsumer { ) external { emit Consumed(_txid, _requestID, gasleft()); if (broken) { - revert('BORKED'); + revert("BORKED"); } } @@ -107,14 +107,14 @@ contract DummyOnDemandSPV is OnDemandSPV { } function _getConfs(bytes32 _header) internal view returns (uint8){ - if (_header == bytes32(0)) { - return OnDemandSPV._getConfs(lastReorgCommonAncestor); - } - return 8; + if (_header == bytes32(0)) { + return OnDemandSPV._getConfs(lastReorgCommonAncestor); + } + return 8; } function getConfsTest() external view returns (uint8) { - return _getConfs(bytes32(0)); + return _getConfs(bytes32(0)); } function checkRequests( From fe51defa5d3b052016328d104b673ab2a5606b81 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Tue, 14 Apr 2020 12:59:39 -0600 Subject: [PATCH 101/148] Summary keeper files (#70) * added some documentation * added more to readme * format changes --- golang/README.md | 29 +++++++++++++++++++++++++++++ golang/x/relay/keeper/headers.go | 11 +++++++++++ golang/x/relay/keeper/links.go | 2 +- golang/x/relay/keeper/requests.go | 3 +++ golang/x/relay/keeper/validator.go | 3 +++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/golang/README.md b/golang/README.md index 87d9d940..f94da98d 100644 --- a/golang/README.md +++ b/golang/README.md @@ -83,3 +83,32 @@ descriptions in the CLI. | MarkNewHeaviest | Mark a new best-known chain tip | | NewRequest | Register a new SPV Proof request | | ProvideProof | Provide a proof that satisfies 1 or more requests | + +## Project Overview + +### Keeper +High-level overview of the project structure within the `keeper` file. + +#### Keeper.go +Instantiates a `keeper` (what handles interaction with the store and contains most of the core functionality of the module). It also handles the genesis state for the relay. + +#### Headers.go +Handles the storage and validation of Bitcoin Headers and Header Chains. + +#### Chain.go +Checks and updates information about the chain. Provides functionality to ensure we are using the heaviest chain. + +#### Links.go +Sets and retrieves data about each link in the chain. This is most commonly used to check information about ancestors. + +#### Requests.go +Stores, retrieves, and validates requests. + +#### Validator.go +Contains validation functions. Currently, this can validate SPV Proofs and Requests. + +#### Handler.go +Handles messages. + +#### Querier.go +Handles queries. diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index e8a6be61..ba0b62e4 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -37,6 +37,7 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) (types. return header, nil } +// getCurrentEpochDifficulty gets the current epoch's difficulty func (k Keeper) getCurrentEpochDifficulty(ctx sdk.Context) sdk.Uint { store := k.getHeaderStore(ctx) result := store.Get([]byte(types.CurrentEpochDiffStorage)) @@ -48,6 +49,7 @@ func (k Keeper) getCurrentEpochDifficulty(ctx sdk.Context) sdk.Uint { return diff } +// setCurrentEpochDifficulty sets the current epoch's difficulty func (k Keeper) setCurrentEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Error { store := k.getHeaderStore(ctx) @@ -60,6 +62,7 @@ func (k Keeper) setCurrentEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Er return nil } +// getPrevEpochDifficulty gets the previous epoch's difficulty func (k Keeper) getPrevEpochDifficulty(ctx sdk.Context) sdk.Uint { store := k.getHeaderStore(ctx) result := store.Get([]byte(types.PrevEpochDiffStorage)) @@ -71,6 +74,7 @@ func (k Keeper) getPrevEpochDifficulty(ctx sdk.Context) sdk.Uint { return diff } +// setPrevEpochDifficulty sets the previous epoch's difficulty func (k Keeper) setPrevEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Error { store := k.getHeaderStore(ctx) @@ -83,6 +87,8 @@ func (k Keeper) setPrevEpochDifficulty(ctx sdk.Context, diff sdk.Uint) sdk.Error return nil } +// updatePrevEpochDifficulty checks if there is a change in difficulty and updates +// the previous epoch's difficulty accordingly func (k Keeper) updatePrevEpochDifficulty(ctx sdk.Context, oldDiff sdk.Uint) sdk.Error { prevEpochDiff := k.getPrevEpochDifficulty(ctx) if prevEpochDiff != oldDiff { @@ -111,6 +117,7 @@ func compareTargets(full, truncated sdk.Uint) bool { return truncatedBI.Cmp(res) == 0 } +// ingestHeader stores a Bitcoin Header func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store := k.getHeaderStore(ctx) @@ -118,6 +125,7 @@ func (k Keeper) ingestHeader(ctx sdk.Context, header types.BitcoinHeader) { store.Set(header.Hash[:], buf) } +// validateHeaderChain validates a chain of Bitcoin Headers func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHeader, internal, isMainnet bool) sdk.Error { prev := anchor // scratchpad, we change this later @@ -163,6 +171,7 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead return nil } +// ingestHeaders validates and stores a chain of Bitcoin Headers func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, internal bool) sdk.Error { anchor, err := k.GetHeader(ctx, headers[0].PrevHash) if err != nil { @@ -184,6 +193,7 @@ func (k Keeper) ingestHeaders(ctx sdk.Context, headers []types.BitcoinHeader, in return nil } +// validateDifficultyChange validates a Header Chain with a difficulty change func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anchor types.BitcoinHeader) sdk.Error { if anchor.Height%2016 != 2015 { return types.ErrWrongEnd(types.DefaultCodespace) @@ -211,6 +221,7 @@ func validateDifficultyChange(headers []types.BitcoinHeader, prevEpochStart, anc return nil } +// ingestDifficultyChange validates and stores a Header Chain with a difficulty change func (k Keeper) ingestDifficultyChange(ctx sdk.Context, prevEpochStartLE types.Hash256Digest, headers []types.BitcoinHeader) sdk.Error { // Find the anchor in our store prevEpochStart, err := k.GetHeader(ctx, prevEpochStartLE) diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index c90d406f..1d918f38 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -46,7 +46,7 @@ func (k Keeper) FindAncestor(ctx sdk.Context, digestLE types.Hash256Digest, offs return current, nil } -// IsAncestor checks whether +// IsAncestor checks if there is a link between an ancestor and header func (k Keeper) IsAncestor(ctx sdk.Context, digestLE, ancestor types.Hash256Digest, limit uint32) bool { current := digestLE diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index 2727c415..aaee5bfa 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -109,6 +109,8 @@ func (k Keeper) getRequest(ctx sdk.Context, id types.RequestID) (types.ProofRequ return request, nil } +// incrementID increments the id used to store a request, +// ID must be in bytes func (k Keeper) incrementID(ctx sdk.Context) sdk.Error { store := k.getRequestStore(ctx) // get id @@ -142,6 +144,7 @@ func (k Keeper) getNextID(ctx sdk.Context) (types.RequestID, sdk.Error) { return newID, nil } +// checkRequests validates a request func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, vin []byte, vout []byte, requestID types.RequestID) sdk.Error { if !btcspv.ValidateVin(vin) { return types.ErrInvalidVin(types.DefaultCodespace) diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go index 6ccef6cf..66656993 100644 --- a/golang/x/relay/keeper/validator.go +++ b/golang/x/relay/keeper/validator.go @@ -16,6 +16,7 @@ func (k Keeper) emitProofProvided( ctx.EventManager().EmitEvent(types.NewProofProvidedEvent(filled.Proof.TxID, filledIDs)) } +// getConfs returns the number of confirmations of any given header func (k Keeper) getConfs(ctx sdk.Context, header types.BitcoinHeader) (uint32, sdk.Error) { bestKnown, err := k.GetBestKnownDigest(ctx) if err != nil { @@ -28,6 +29,7 @@ func (k Keeper) getConfs(ctx sdk.Context, header types.BitcoinHeader) (uint32, s return bestKnownHeader.Height - header.Height, nil } +// validateProof validates an SPV Proof and checks that it is stored correctly func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { // If it is not valid, it will return an error _, err := proof.Validate() @@ -47,6 +49,7 @@ func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { return nil } +// checkRequestsFilled validates a filledRequest func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.FilledRequests) sdk.Error { // Validate Proof once err := k.validateProof(ctx, filledRequests.Proof) From f0219d165c81c63d940a8f70451dc7cd6bb6fe5e Mon Sep 17 00:00:00 2001 From: James Prestwich <10149425+prestwich@users.noreply.github.com> Date: Tue, 14 Apr 2020 13:26:28 -0700 Subject: [PATCH 102/148] Proof handler interface (#73) * # This is a combination of 3 commits. # This is the 1st commit message: feature: ProofHandler is an interface, and is set on relay instantiation # This is the commit message #2: feature: handleMsgProvideProof dispatches valid proofs to the handler # This is the commit message #3: lint: fix linting errors * feature: ProofHandler is an interface, and is set on relay instantiation feature: handleMsgProvideProof dispatches valid proofs to the handler lint: fix linting errors feature: HandleValidProof also receives a slice of filled ProofRequests feature: ProofHandler is an interface, and is set on relay instantiation feature: handleMsgProvideProof dispatches valid proofs to the handler lint: fix linting errors feature: HandleValidProof also receives a slice of filled ProofRequests --- golang/EXTENDING.md | 28 ++++++++++++++++++++++--- golang/README.md | 2 +- golang/fake_app.go | 3 ++- golang/x/relay/alias.go | 6 ++++++ golang/x/relay/keeper/handler.go | 6 ++++-- golang/x/relay/keeper/keeper.go | 16 +++++++------- golang/x/relay/keeper/keeper_test.go | 2 +- golang/x/relay/keeper/querier.go | 2 +- golang/x/relay/keeper/validator.go | 19 ++++++++++------- golang/x/relay/keeper/validator_test.go | 8 +++---- golang/x/relay/types/types.go | 17 +++++++++++++++ 11 files changed, 81 insertions(+), 28 deletions(-) diff --git a/golang/EXTENDING.md b/golang/EXTENDING.md index f5b0eea5..f50825c7 100644 --- a/golang/EXTENDING.md +++ b/golang/EXTENDING.md @@ -7,14 +7,36 @@ other functionality should likely be put into a separate module. ### Integrating with other modules -An instance of `relay.Keeper` should be added to your app in `app.go`. It can -be instantiated as follows: +The relay keeper keeps a reference to an object that implements the following +interface (found in `x/types/types.go`). +```go +type ProofHandler interface { + HandleValidProof(ctx sdk.Context, filled FilledRequests, requests []ProofRequest) +} ``` + +The `FilledRequests` struct contains an `SPVProof` and supporting information +about the transaction that fulfills the request. +It can be found in `x/types/validator.go`. `requests []ProofRequest` is a slice +of `ProofRequest`s that have been filled. + +When the keeper validates a proof, it will call the `HandleValidProof` function +with the valid `FilledRequests` struct and the `ProofRequests` that have been +filled. + +First, instantiate a `handler` that fulfills the `ProofHandler` interface. Then +add an instance of `relay.Keeper` to your app in `app.go`. It can be +instantiated as follows: + +```go +handler = types.NewNullHandler() // or your preferred handler + app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], app.cdc, - true, // Mainnet // TODO: pass this in somehow + true, + handler ) ``` diff --git a/golang/README.md b/golang/README.md index f94da98d..c59c1c97 100644 --- a/golang/README.md +++ b/golang/README.md @@ -44,7 +44,7 @@ Instructions for setting up manual testing can be found in the README in - - [X] Integration Tests - - [ ] Document relay design & architecture - - [X] Document public interface -- - [ ] Provide hooks to execute tasks + dispatch messages +- - [X] Provide hooks to execute tasks + dispatch messages - - [ ] Add a basic web dashboard with Relay health diff --git a/golang/fake_app.go b/golang/fake_app.go index ae9212f4..f05ce6f7 100644 --- a/golang/fake_app.go +++ b/golang/fake_app.go @@ -195,7 +195,8 @@ func NewRelayApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseA app.relayKeeper = relay.NewKeeper( keys[relay.StoreKey], app.cdc, - true, // Mainnet // TODO: pass this in somehow + true, // Mainnet // TODO: pass this in somehow + relay.NullHandler{}, // Proof Handler. real apps should fill this in ) app.mm = module.NewManager( diff --git a/golang/x/relay/alias.go b/golang/x/relay/alias.go index aee17ded..cd42d9ff 100644 --- a/golang/x/relay/alias.go +++ b/golang/x/relay/alias.go @@ -58,4 +58,10 @@ type ( // SPVProof is the base struct for an SPV proof SPVProof = types.SPVProof + + // ProofHandler is an interface to which the keepers dispatches valid proofs + ProofHandler = types.ProofHandler + + // NullHandler does nothing + NullHandler = types.NullHandler ) diff --git a/golang/x/relay/keeper/handler.go b/golang/x/relay/keeper/handler.go index 42164453..60150993 100644 --- a/golang/x/relay/keeper/handler.go +++ b/golang/x/relay/keeper/handler.go @@ -78,12 +78,14 @@ func handleMsgNewRequest(ctx sdk.Context, keeper Keeper, msg types.MsgNewRequest } func handleMsgProvideProof(ctx sdk.Context, keeper Keeper, msg types.MsgProvideProof) sdk.Result { - err := keeper.checkRequestsFilled(ctx, msg.Filled) + filled, err := keeper.checkRequestsFilled(ctx, msg.Filled) if err != nil { return err.Result() } - // TODO: Add "hooks" + // Dispatch the proof to the keeper's proof handler + keeper.ProofHandler.HandleValidProof(ctx, msg.Filled, filled) + return sdk.Result{ Events: ctx.EventManager().Events(), } diff --git a/golang/x/relay/keeper/keeper.go b/golang/x/relay/keeper/keeper.go index ab6199d1..e49e4b29 100644 --- a/golang/x/relay/keeper/keeper.go +++ b/golang/x/relay/keeper/keeper.go @@ -11,17 +11,19 @@ import ( // Keeper maintains the link to data storage and exposes getter/setter methods for the various parts of the state machine type Keeper struct { - storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context - cdc *codec.Codec // The wire codec for binary encoding/decoding. - IsMainNet bool + storeKey sdk.StoreKey // Unexposed key to access store from sdk.Context + cdc *codec.Codec // The wire codec for binary encoding/decoding. + IsMainNet bool + ProofHandler types.ProofHandler } // NewKeeper instantiates a new keeper -func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec, mainnet bool) Keeper { +func NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec, mainnet bool, handler types.ProofHandler) Keeper { return Keeper{ - storeKey: storeKey, - cdc: cdc, - IsMainNet: mainnet, + storeKey: storeKey, + cdc: cdc, + IsMainNet: mainnet, + ProofHandler: handler, } } diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 748dc74d..4d5d5e1b 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -234,7 +234,7 @@ func (s *KeeperSuite) InitTestContext(mainnet, isCheckTx bool) { cdc := codec.New() ctx := sdk.NewContext(ms, abci.Header{ChainID: "relayTestChain"}, isCheckTx, tmlog.NewNopLogger()) - keeper := NewKeeper(relayKey, cdc, mainnet) + keeper := NewKeeper(relayKey, cdc, mainnet, types.NewNullHandler()) s.Context = ctx s.Keeper = keeper diff --git a/golang/x/relay/keeper/querier.go b/golang/x/relay/keeper/querier.go index a95454d1..cd0036d1 100644 --- a/golang/x/relay/keeper/querier.go +++ b/golang/x/relay/keeper/querier.go @@ -272,7 +272,7 @@ func queryCheckRequests(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) ( } // This calls the keeper with the parsed arguments, and gets an answer - resErr := keeper.checkRequestsFilled(ctx, params.Filled) + _, resErr := keeper.checkRequestsFilled(ctx, params.Filled) if resErr != nil { valid = false errMsg = resErr.Error() diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go index 66656993..29f1c5bf 100644 --- a/golang/x/relay/keeper/validator.go +++ b/golang/x/relay/keeper/validator.go @@ -49,28 +49,29 @@ func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { return nil } -// checkRequestsFilled validates a filledRequest -func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.FilledRequests) sdk.Error { +func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.FilledRequests) ([]types.ProofRequest, sdk.Error) { // Validate Proof once err := k.validateProof(ctx, filledRequests.Proof) if err != nil { - return err + return nil, err } confs, confsErr := k.getConfs(ctx, filledRequests.Proof.ConfirmingHeader) if confsErr != nil { - return confsErr + return nil, confsErr } + var filled []types.ProofRequest + for i := range filledRequests.Filled { // get request request, getErr := k.getRequest(ctx, filledRequests.Filled[i].ID) if getErr != nil { - return getErr + return nil, getErr } // check confirmations if confs < uint32(request.NumConfs) { - return types.ErrNotEnoughConfs(types.DefaultCodespace) + return nil, types.ErrNotEnoughConfs(types.DefaultCodespace) } // check request @@ -82,10 +83,12 @@ func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.Filled filledRequests.Proof.Vout, filledRequests.Filled[i].ID) if err != nil { - return err + return nil, err } + + filled = append(filled, request) } k.emitProofProvided(ctx, filledRequests) - return nil + return filled, nil } diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go index e8f33707..8ab74508 100644 --- a/golang/x/relay/keeper/validator_test.go +++ b/golang/x/relay/keeper/validator_test.go @@ -71,7 +71,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { s.Nil(requestErr) // errors if getConfs fails - err := s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) + _, err := s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) s.Equal(sdk.CodeType(105), err.Code()) s.Keeper.setBestKnownDigest(s.Context, validProof.BestKnown.Hash) @@ -81,7 +81,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { activeErr := s.Keeper.setRequestState(s.Context, types.RequestID{}, false) s.SDKNil(activeErr) - err = s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) + _, err = s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) s.Equal(sdk.CodeType(606), err.Code()) // reactivate request @@ -89,7 +89,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { s.SDKNil(activeErr) for i := range tc { - err := s.Keeper.checkRequestsFilled(s.Context, tc[i].FilledRequests) + _, err := s.Keeper.checkRequestsFilled(s.Context, tc[i].FilledRequests) if tc[i].Error != 0 { s.Equal(sdk.CodeType(tc[i].Error), err.Code()) } else { @@ -103,6 +103,6 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { copiedRequest := tc[0].FilledRequests copiedRequest.Filled[0].ID = types.RequestID{0, 0, 0, 0, 0, 0, 0, 1} - err = s.Keeper.checkRequestsFilled(s.Context, copiedRequest) + _, err = s.Keeper.checkRequestsFilled(s.Context, copiedRequest) s.Equal(sdk.CodeType(611), err.Code()) } diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index bfe8c45e..86cdaa60 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -8,6 +8,11 @@ import ( "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) +// ProofHandler is an interface to which the keeper dispatches valid proofs +type ProofHandler interface { + HandleValidProof(ctx sdk.Context, filled FilledRequests, requests []ProofRequest) +} + // Hash256Digest 32-byte double-sha2 digest type Hash256Digest = btcspv.Hash256Digest @@ -53,3 +58,15 @@ func Hash256DigestFromHex(hexStr string) (Hash256Digest, sdk.Error) { } return digest, nil } + +// NullHandler does nothing +type NullHandler struct{} + +// HandleValidProof handles a valid proof (by doing nothing) +func (n NullHandler) HandleValidProof(ctx sdk.Context, filled FilledRequests, requests []ProofRequest) { +} + +// NewNullHandler instantiates a new null handler +func NewNullHandler() NullHandler { + return NullHandler{} +} From 558ba2c8f94e5db8535735e906787666b7061361 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Wed, 15 Apr 2020 17:18:04 -0400 Subject: [PATCH 103/148] Fix TravisCI lint race condition (#77) --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3748ab35..1b6c09cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,9 @@ matrix: before_script: - cd golang - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.20.0 - - make + - make install script: + - make lint - go test ./x/relay/keeper - go test ./x/relay/types - go test -mod=readonly `go list ./cli_test/...` @@ -19,6 +20,6 @@ matrix: - cd solidity - npm install -g truffle - npm install - - npm run lint script: + - npm run lint - npm run test From 99063da4325a532f33c1dd7bf4b5498bc738ea48 Mon Sep 17 00:00:00 2001 From: Erin Hales <36751902+ErinHales@users.noreply.github.com> Date: Wed, 15 Apr 2020 15:40:23 -0600 Subject: [PATCH 104/148] use Strip0xPrefix from btcspv (#75) --- golang/x/relay/types/requests.go | 3 ++- golang/x/relay/types/utils.go | 11 ----------- 2 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 golang/x/relay/types/utils.go diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go index 52756e53..b64ef81f 100644 --- a/golang/x/relay/types/requests.go +++ b/golang/x/relay/types/requests.go @@ -7,6 +7,7 @@ import ( "strconv" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/bitcoin-spv/golang/btcspv" ) // RequestID is an 8 byte id used to store requests @@ -66,7 +67,7 @@ func RequestIDFromString(s string) (RequestID, error) { // UnmarshalJSON unmarshalls 8 byte requestID func (r *RequestID) UnmarshalJSON(b []byte) error { // Have to trim quotation marks off byte array - buf, err := hex.DecodeString(strip0xPrefix(string(b[1 : len(b)-1]))) + buf, err := hex.DecodeString(btcspv.Strip0xPrefix(string(b[1 : len(b)-1]))) if err != nil { return err } diff --git a/golang/x/relay/types/utils.go b/golang/x/relay/types/utils.go deleted file mode 100644 index 87a769b9..00000000 --- a/golang/x/relay/types/utils.go +++ /dev/null @@ -1,11 +0,0 @@ -package types - -func strip0xPrefix(s string) string { - if len(s) < 2 { - return s - } - if s[0:2] == "0x" { - return s[2:] - } - return s -} From bea8b7da5f1eaa46e8f1ebc7e549e57477a9cb88 Mon Sep 17 00:00:00 2001 From: James Prestwich Date: Wed, 15 Apr 2020 15:01:18 -0700 Subject: [PATCH 105/148] chore: update README to mention golang version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b80c57e..34e59c9e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ This is a Bitcoin Relay. It uses 1 + 1/n slots per header relayed (n is currently 4), and 2 slots to externalize useful information (best chain tip and best shared ancestor of latest reorg). -At present, only a Solidity implementation is available, but we intend to add -more implementations soon :) +Implementations are available in Solidity (for EVM chains) and Golang using the +cosmos-sdk framework. ### How does it work? From 38654bbf634689af118f74f794e558c047c0ec74 Mon Sep 17 00:00:00 2001 From: Emily Williams Date: Thu, 16 Apr 2020 15:16:27 -0400 Subject: [PATCH 106/148] Add more details to Error messages (#76) * Update Errors with more detailed messages Squashing errors * Use digest format for all UnknownBlockErr * Split ErrBadHeight into 2 distinct messages * Add new error: ErrBadOffset * TestVectors 103 --> 104 * Fix tests * linting * dumb typos --- golang/x/relay/keeper/chain.go | 24 ++-- golang/x/relay/keeper/chain_test.go | 6 +- golang/x/relay/keeper/handler_test.go | 12 +- golang/x/relay/keeper/headers.go | 6 +- golang/x/relay/keeper/headers_test.go | 10 +- golang/x/relay/keeper/keeper_test.go | 2 +- golang/x/relay/keeper/links.go | 4 +- golang/x/relay/keeper/querier_test.go | 12 +- golang/x/relay/keeper/request_test.go | 2 +- golang/x/relay/keeper/requests.go | 6 +- golang/x/relay/keeper/validator.go | 4 +- golang/x/relay/keeper/validator_test.go | 16 +-- golang/x/relay/types/errors.go | 159 ++++++++++-------------- golang/x/relay/types/msgs.go | 8 +- golang/x/relay/types/requests.go | 2 +- golang/x/relay/types/types.go | 2 +- testVectors.json | 18 +-- 17 files changed, 134 insertions(+), 159 deletions(-) diff --git a/golang/x/relay/keeper/chain.go b/golang/x/relay/keeper/chain.go index 6438cd0f..450966c5 100644 --- a/golang/x/relay/keeper/chain.go +++ b/golang/x/relay/keeper/chain.go @@ -21,7 +21,7 @@ func (k Keeper) getDigestByStoreKey(ctx sdk.Context, key string) (types.Hash256D digest, err := btcspv.NewHash256Digest(result) if err != nil { - return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrBadHash256Digest(types.DefaultCodespace, key) } return digest, nil } @@ -92,19 +92,23 @@ func (k Keeper) IsMostRecentCommonAncestor(ctx sdk.Context, ancestor, left, righ func (k Keeper) HeaviestFromAncestor(ctx sdk.Context, ancestor, currentBest, newBest types.Hash256Digest, limit uint32) (types.Hash256Digest, sdk.Error) { ancestorBlock, err := k.GetHeader(ctx, ancestor) if err != nil { - return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace, "ancestor", ancestor) } leftBlock, err := k.GetHeader(ctx, currentBest) if err != nil { - return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace, "currentBest", currentBest) } rightBlock, err := k.GetHeader(ctx, newBest) if err != nil { - return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace, "newBest", newBest) } - if leftBlock.Height < ancestorBlock.Height || rightBlock.Height < ancestorBlock.Height { - return types.Hash256Digest{}, types.ErrBadHeight(types.DefaultCodespace) + if leftBlock.Height < ancestorBlock.Height { + return types.Hash256Digest{}, types.ErrBadHeight(types.DefaultCodespace, "currentBest", currentBest) + } + + if rightBlock.Height < ancestorBlock.Height { + return types.Hash256Digest{}, types.ErrBadHeight(types.DefaultCodespace, "newBest", newBest) } nextPeriodStartHeight := ancestorBlock.Height + 2016 - (ancestorBlock.Height % 2016) @@ -150,16 +154,16 @@ func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, c currentBestDigest := btcspv.Hash256(currentBest[:]) if !k.HasHeader(ctx, newBestDigest) { - return types.ErrUnknownBlock(types.DefaultCodespace) + return types.ErrUnknownBlock(types.DefaultCodespace, "newBest", newBestDigest) } knownBestDigest, err := k.GetBestKnownDigest(ctx) if err != nil || currentBestDigest != knownBestDigest { - return types.ErrNotBestKnown(types.DefaultCodespace) + return types.ErrNotBestKnown(types.DefaultCodespace, currentBestDigest, knownBestDigest) } if !k.IsMostRecentCommonAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) { - return types.ErrNotHeaviestAncestor(types.DefaultCodespace) + return types.ErrNotHeaviestAncestor(types.DefaultCodespace, ancestor) } better, err := k.HeaviestFromAncestor(ctx, ancestor, knownBestDigest, newBestDigest, limit) @@ -168,7 +172,7 @@ func (k Keeper) MarkNewHeaviest(ctx sdk.Context, ancestor types.Hash256Digest, c } if newBestDigest != better { - return types.ErrNotHeavier(types.DefaultCodespace) + return types.ErrNotHeavier(types.DefaultCodespace, newBestDigest, better) } // get newBestHeader diff --git a/golang/x/relay/keeper/chain_test.go b/golang/x/relay/keeper/chain_test.go index 048043c1..c7ebb85d 100644 --- a/golang/x/relay/keeper/chain_test.go +++ b/golang/x/relay/keeper/chain_test.go @@ -24,7 +24,7 @@ func (s *KeeperSuite) TestGetDigestByStoreKey() { store.Set([]byte(key), wrongLenDigest) _, err := s.Keeper.getDigestByStoreKey(s.Context, key) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) } func (s *KeeperSuite) TestGetBestKnownDigest() { @@ -121,7 +121,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { pre[1].Raw, 10, ) - s.Equal(sdk.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) err = s.Keeper.IngestHeaderChain(s.Context, pre) s.SDKNil(err) @@ -146,7 +146,7 @@ func (s *KeeperSuite) TestMarkNewHeaviest() { pre[1].Raw, 10, ) - s.Equal(sdk.CodeType(404), err.Code()) + s.Equal(sdk.CodeType(types.NotHeaviestAncestor), err.Code()) for i := range tc { s.Keeper.setBestKnownDigest(s.Context, tc[i].BestKnownDigest) diff --git a/golang/x/relay/keeper/handler_test.go b/golang/x/relay/keeper/handler_test.go index ab586d32..0291719e 100644 --- a/golang/x/relay/keeper/handler_test.go +++ b/golang/x/relay/keeper/handler_test.go @@ -3,8 +3,8 @@ package keeper import ( "bytes" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) func getAccAddress() sdk.AccAddress { @@ -45,7 +45,7 @@ func (s *KeeperSuite) TestHandleMsgIngestHeaderChain() { newMsg := types.NewMsgIngestHeaderChain(getAccAddress(), testCases[0].Headers) res := handler(s.Context, newMsg) - s.Equal(sdk.CodeType(103), res.Code) + s.Equal(sdk.CodeType(types.UnknownBlock), res.Code) s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) res = handler(s.Context, newMsg) @@ -59,7 +59,7 @@ func (s *KeeperSuite) TestHandleMsgIngestDifficultyChange() { newMsg := types.NewMsgIngestDifficultyChange(getAccAddress(), testCases[0].PrevEpochStart.Hash, testCases[0].Headers) res := handler(s.Context, newMsg) - s.Equal(sdk.CodeType(103), res.Code) + s.Equal(sdk.CodeType(types.UnknownBlock), res.Code) s.Keeper.ingestHeader(s.Context, testCases[0].PrevEpochStart) s.Keeper.ingestHeader(s.Context, testCases[0].Anchor) @@ -101,7 +101,7 @@ func (s *KeeperSuite) TestHandleMarkNewHeaviest() { // returns correct error newMsg := types.NewMsgMarkNewHeaviest(getAccAddress(), tv.OldPeriodStart.Hash, tv.OldPeriodStart.Raw, tv.OldPeriodStart.Raw, 10) res := handler(s.Context, newMsg) - s.Equal(sdk.CodeType(403), res.Code) + s.Equal(sdk.CodeType(types.NotBestKnown), res.Code) // Successfully marks new heaviest newMsg = types.NewMsgMarkNewHeaviest(getAccAddress(), tv.Genesis.Hash, tv.Genesis.Raw, pre[0].Raw, 10) @@ -122,7 +122,7 @@ func (s *KeeperSuite) TestHandleNewRequest() { // Msg validation failed newRequest = types.NewMsgNewRequest(getAccAddress(), []byte{0}, []byte{0}, 0, 0, types.Local, nil) res = handler(s.Context, newRequest) - s.Equal(sdk.CodeType(602), res.Code) + s.Equal(sdk.CodeType(types.SpendsLength), res.Code) // setRequest error store := s.Keeper.getRequestStore(s.Context) @@ -130,5 +130,5 @@ func (s *KeeperSuite) TestHandleNewRequest() { newRequest = types.NewMsgNewRequest(getAccAddress(), bytes.Repeat([]byte{0}, 36), []byte{0}, 0, 0, types.Local, nil) res = handler(s.Context, newRequest) - s.Equal(sdk.CodeType(107), res.Code) + s.Equal(sdk.CodeType(types.BadHexLen), res.Code) } diff --git a/golang/x/relay/keeper/headers.go b/golang/x/relay/keeper/headers.go index ba0b62e4..8aeb0a6f 100644 --- a/golang/x/relay/keeper/headers.go +++ b/golang/x/relay/keeper/headers.go @@ -28,7 +28,7 @@ func (k Keeper) GetHeader(ctx sdk.Context, digestLE types.Hash256Digest) (types. store := k.getHeaderStore(ctx) if !store.Has(digestLE[:]) { - return types.BitcoinHeader{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.BitcoinHeader{}, types.ErrUnknownBlock(types.DefaultCodespace, "digest", digestLE) } buf := store.Get(digestLE[:]) @@ -147,13 +147,13 @@ func validateHeaderChain(anchor types.BitcoinHeader, headers []types.BitcoinHead // ensure height changes as expected if prev.Height != header.Height-1 { - return types.ErrBadHeight(types.DefaultCodespace) + return types.ErrHeightMismatch(types.DefaultCodespace, prev.Hash, header.Hash) } // ensure expectedTarget doesn't change // it's allowed to change if the relay is in testnet mode if isMainnet && !btcspv.ExtractTarget(header.Raw).Equal(expectedTarget) { - return types.ErrUnexpectedRetarget(types.DefaultCodespace) + return types.ErrUnexpectedRetarget(types.DefaultCodespace, header.Raw) } // copy header raw into a bytearray diff --git a/golang/x/relay/keeper/headers_test.go b/golang/x/relay/keeper/headers_test.go index 287b357f..8b2e5e0e 100644 --- a/golang/x/relay/keeper/headers_test.go +++ b/golang/x/relay/keeper/headers_test.go @@ -1,7 +1,7 @@ package keeper import ( - "github.com/cosmos/cosmos-sdk/types" + "github.com/summa-tx/relays/golang/x/relay/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,7 +9,7 @@ func (s *KeeperSuite) TestGetHeader() { // errors if header is not found header := s.Fixtures.HeaderTestCases.ValidateChain[0].Headers[0] _, err := s.Keeper.GetHeader(s.Context, header.Hash) - s.Equal(types.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) } func (s *KeeperSuite) TestEmitExtension() { @@ -42,7 +42,7 @@ func (s *KeeperSuite) TestIngestHeaders() { // errors if anchor is not found err := s.Keeper.ingestHeaders(s.Context, cases[0].Headers, cases[0].Internal) - s.Equal(types.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) for _, tc := range cases { s.InitTestContext(tc.IsMainnet, false) @@ -111,12 +111,12 @@ func (s *KeeperSuite) TestIngestDifficultyChange() { // errors if PrevEpochStart is not found err := s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.Hash, cases[0].Headers) - s.Equal(types.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) // errors if anchor is not found s.Keeper.ingestHeader(s.Context, cases[0].PrevEpochStart) err = s.Keeper.IngestDifficultyChange(s.Context, cases[0].PrevEpochStart.Hash, cases[0].Headers) - s.Equal(types.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) for _, tc := range cases { s.Keeper.ingestHeader(s.Context, tc.PrevEpochStart) diff --git a/golang/x/relay/keeper/keeper_test.go b/golang/x/relay/keeper/keeper_test.go index 4d5d5e1b..ddca5c1b 100644 --- a/golang/x/relay/keeper/keeper_test.go +++ b/golang/x/relay/keeper/keeper_test.go @@ -8,6 +8,7 @@ import ( "os" "testing" + "github.com/summa-tx/relays/golang/x/relay/types" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" @@ -22,7 +23,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/summa-tx/bitcoin-spv/golang/btcspv" - "github.com/summa-tx/relays/golang/x/relay/types" ) type NamedCase interface { diff --git a/golang/x/relay/keeper/links.go b/golang/x/relay/keeper/links.go index 1d918f38..e788c92b 100644 --- a/golang/x/relay/keeper/links.go +++ b/golang/x/relay/keeper/links.go @@ -33,13 +33,13 @@ func (k Keeper) getLink(ctx sdk.Context, digestLE types.Hash256Digest) types.Has func (k Keeper) FindAncestor(ctx sdk.Context, digestLE types.Hash256Digest, offset uint32) (types.Hash256Digest, sdk.Error) { current := digestLE if !k.hasLink(ctx, current) { - return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace, "digest", digestLE) } for i := uint32(0); i < offset; i++ { current = k.getLink(ctx, current) if !k.hasLink(ctx, current) { - return types.Hash256Digest{}, types.ErrUnknownBlock(types.DefaultCodespace) + return types.Hash256Digest{}, types.ErrBadOffset(types.DefaultCodespace, current) } } diff --git a/golang/x/relay/keeper/querier_test.go b/golang/x/relay/keeper/querier_test.go index 0f0e87ae..95c1f9da 100644 --- a/golang/x/relay/keeper/querier_test.go +++ b/golang/x/relay/keeper/querier_test.go @@ -3,8 +3,8 @@ package keeper import ( "encoding/json" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" + sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" ) @@ -153,7 +153,7 @@ func (s *KeeperSuite) TestQueryGetRelayGenesis() { // Test that GetRelayGenesis errors if RelayGenesis is not found _, err := querier(s.Context, path, req) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) // Set Genesis state err = s.Keeper.SetGenesisState(s.Context, genesis, epochStart) @@ -185,7 +185,7 @@ func (s *KeeperSuite) TestQueryGetLastReorgLCA() { // Test that it errors if it doesn't find LastReorgLCA _, err := querier(s.Context, path, req) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) setStateErr := s.Keeper.SetGenesisState(s.Context, genesis, epochStart) s.SDKNil(setStateErr) @@ -220,7 +220,7 @@ func (s *KeeperSuite) TestQueryFindAncestor() { // Test that it errors if ancestor is not found _, findAncestorErr := querier(s.Context, path, req) - s.Equal(sdk.CodeType(103), findAncestorErr.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), findAncestorErr.Code()) // initialize data s.Keeper.ingestHeader(s.Context, anchor) @@ -305,7 +305,7 @@ func (s *KeeperSuite) TestQueryHeaviestFromAncestor() { } _, err = querier(s.Context, path, req) - s.Equal(sdk.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) // Test that default limit is used if limit is set to zero params = types.QueryParamsHeaviestFromAncestor{ @@ -445,7 +445,7 @@ func (s *KeeperSuite) TestQueryGetRequest() { // Errors if request is not found _, err = querier(s.Context, path, req) - s.Equal(sdk.CodeType(601), err.Code()) + s.Equal(sdk.CodeType(types.UnknownRequest), err.Code()) // Set Request err = s.Keeper.setRequest(s.Context, []byte{0}, []byte{0}, 0, 0, types.Local, nil) diff --git a/golang/x/relay/keeper/request_test.go b/golang/x/relay/keeper/request_test.go index 23949461..b6b49533 100644 --- a/golang/x/relay/keeper/request_test.go +++ b/golang/x/relay/keeper/request_test.go @@ -3,9 +3,9 @@ package keeper import ( "bytes" + "github.com/summa-tx/relays/golang/x/relay/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/bitcoin-spv/golang/btcspv" - "github.com/summa-tx/relays/golang/x/relay/types" ) func (s *KeeperSuite) TestEmitProofRequest() { diff --git a/golang/x/relay/keeper/requests.go b/golang/x/relay/keeper/requests.go index aaee5bfa..3f4b5c64 100644 --- a/golang/x/relay/keeper/requests.go +++ b/golang/x/relay/keeper/requests.go @@ -168,11 +168,11 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, v // hash the output script (out[8:]) outDigest := btcspv.Hash256(out[8:]) if outDigest != req.Pays { - return types.ErrRequestPays(types.DefaultCodespace) + return types.ErrRequestPays(types.DefaultCodespace, requestID) } paysValue := req.PaysValue if paysValue != 0 && uint64(btcspv.ExtractValue(out)) < paysValue { - return types.ErrRequestValue(types.DefaultCodespace) + return types.ErrRequestValue(types.DefaultCodespace, requestID) } } @@ -185,7 +185,7 @@ func (k Keeper) checkRequests(ctx sdk.Context, inputIndex, outputIndex uint32, v outpoint := btcspv.ExtractOutpoint(in) inDigest := btcspv.Hash256(outpoint) if hasSpends && inDigest != req.Spends { - return types.ErrRequestSpends(types.DefaultCodespace) + return types.ErrRequestSpends(types.DefaultCodespace, requestID) } } return nil diff --git a/golang/x/relay/keeper/validator.go b/golang/x/relay/keeper/validator.go index 29f1c5bf..75ff5b34 100644 --- a/golang/x/relay/keeper/validator.go +++ b/golang/x/relay/keeper/validator.go @@ -43,7 +43,7 @@ func (k Keeper) validateProof(ctx sdk.Context, proof types.SPVProof) sdk.Error { } isAncestor := k.IsAncestor(ctx, proof.ConfirmingHeader.Hash, lca, 240) if !isAncestor { - return types.ErrNotAncestor(types.DefaultCodespace) + return types.ErrNotAncestor(types.DefaultCodespace, proof.ConfirmingHeader.Hash) } return nil @@ -71,7 +71,7 @@ func (k Keeper) checkRequestsFilled(ctx sdk.Context, filledRequests types.Filled } // check confirmations if confs < uint32(request.NumConfs) { - return nil, types.ErrNotEnoughConfs(types.DefaultCodespace) + return nil, types.ErrNotEnoughConfs(types.DefaultCodespace, filledRequests.Filled[i].ID) } // check request diff --git a/golang/x/relay/keeper/validator_test.go b/golang/x/relay/keeper/validator_test.go index 8ab74508..4ab0a435 100644 --- a/golang/x/relay/keeper/validator_test.go +++ b/golang/x/relay/keeper/validator_test.go @@ -1,8 +1,8 @@ package keeper import ( - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/summa-tx/relays/golang/x/relay/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) func (s *KeeperSuite) TestGetConfs() { @@ -11,14 +11,14 @@ func (s *KeeperSuite) TestGetConfs() { // errors if Best Known Digest is not found confs, err := s.Keeper.getConfs(s.Context, header) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) s.Equal(uint32(0), confs) // errors if Best Known Digest header is not found s.Keeper.setBestKnownDigest(s.Context, bestKnown.Hash) confs, err = s.Keeper.getConfs(s.Context, header) - s.Equal(sdk.CodeType(103), err.Code()) + s.Equal(sdk.CodeType(types.UnknownBlock), err.Code()) s.Equal(uint32(0), confs) // success @@ -35,13 +35,13 @@ func (s *KeeperSuite) TestValidateProof() { // errors if LCA is not found err := s.Keeper.validateProof(s.Context, proof) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) // errors if link is not found s.Keeper.setLastReorgLCA(s.Context, proofCases[0].LCA) err = s.Keeper.validateProof(s.Context, proof) - s.Equal(sdk.CodeType(610), err.Code()) + s.Equal(sdk.CodeType(types.NotAncestor), err.Code()) for i := range proofCases { // Store lots of stuff @@ -72,7 +72,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { // errors if getConfs fails _, err := s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) - s.Equal(sdk.CodeType(105), err.Code()) + s.Equal(sdk.CodeType(types.BadHash256Digest), err.Code()) s.Keeper.setBestKnownDigest(s.Context, validProof.BestKnown.Hash) @@ -82,7 +82,7 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { s.SDKNil(activeErr) _, err = s.Keeper.checkRequestsFilled(s.Context, tc[0].FilledRequests) - s.Equal(sdk.CodeType(606), err.Code()) + s.Equal(sdk.CodeType(types.ClosedRequest), err.Code()) // reactivate request activeErr = s.Keeper.setRequestState(s.Context, types.RequestID{}, true) @@ -104,5 +104,5 @@ func (s *KeeperSuite) TestCheckRequestsFilled() { copiedRequest := tc[0].FilledRequests copiedRequest.Filled[0].ID = types.RequestID{0, 0, 0, 0, 0, 0, 0, 1} _, err = s.Keeper.checkRequestsFilled(s.Context, copiedRequest) - s.Equal(sdk.CodeType(611), err.Code()) + s.Equal(sdk.CodeType(types.NotEnoughConfs), err.Code()) } diff --git a/golang/x/relay/types/errors.go b/golang/x/relay/types/errors.go index c461564d..2db5d57f 100644 --- a/golang/x/relay/types/errors.go +++ b/golang/x/relay/types/errors.go @@ -12,40 +12,35 @@ const ( // 100-block -- shared errors - // UnknownError is an unknown error - UnknownError sdk.CodeType = 100 - // UnknownErrorMessage is the corresponding message - UnknownErrorMessage = "Unknown Error" - // BadHeaderLength means Header array length not divisible by 80 BadHeaderLength sdk.CodeType = 101 // BadHeaderLengthMessage is the corresponding message - BadHeaderLengthMessage = "Header array length must be divisible by 80" + BadHeaderLengthMessage = "Header array length must be divisble by 80 but header labeled %s with header %x has length %d" + + // BadHeight occurs when a proposed descendant is below a proposed ancestor + BadHeight sdk.CodeType = 102 + // BadHeightMessage is the corresponding message + BadHeightMessage = "Block labeled %s with digest %x is below the ancestor height" - // InsufficientWork means a block did not have sufficient work - InsufficientWork sdk.CodeType = 102 - // InsufficientWorkMessage is the corresponding message - InsufficientWorkMessage = "Header work is insufficient" + // HeightMismatch occurs when blocks do not have have consecutive height increments + HeightMismatch sdk.CodeType = 103 + // HeightMismatchMessage is the corresponding message + HeightMismatchMessage = "Height mismatch between blocks %x and %x" // UnknownBlock is the error code for unknown blocks - UnknownBlock sdk.CodeType = 103 + UnknownBlock sdk.CodeType = 104 // UnknownBlockMessage is the corresponding message - UnknownBlockMessage = "Unknown block" - - // BadHeight occurs when a proposed descendant is below a proposed ancestor - BadHeight sdk.CodeType = 104 - // BadHeightMessage is the corresponding message - BadHeightMessage = "A descendant height is below the ancestor height" + UnknownBlockMessage = "Unknown block labeled %s with digest %x" // BadHash256Digest occurs when a wrong-length hash256 digest is found BadHash256Digest sdk.CodeType = 105 // BadHash256DigestMessage is the corresponding message - BadHash256DigestMessage = "Digest had wrong length" + BadHash256DigestMessage = "Digest %s had wrong length" // BadHex occurs when a hex argument couldn't be deserialized BadHex sdk.CodeType = 106 // BadHexMessage is the corresponding message - BadHexMessage = "Bad hex string in query or msg" + BadHexMessage = "Bad hex string in query or msg: %s" // BadHexLen occurs when a hex argument is the wrong length BadHexLen sdk.CodeType = 107 @@ -61,17 +56,17 @@ const ( // AlreadyInitMessage is the corresponding message AlreadyInitMessage = "Relay has already set genesis state" + // BadOffset occurs when chain has traversed to block with no link + BadOffset sdk.CodeType = 110 + // BadOffsetMessage is the corresponding message + BadOffsetMessage = "Reached bottom of relay chain: block with digest %x has no link" + // 200-block -- AddHeaders // UnexpectedRetarget indicates a retarget was seen during AddHeaders loop UnexpectedRetarget sdk.CodeType = 201 // UnexpectedRetargetMessage is the corresponding message - UnexpectedRetargetMessage = "Target changed unexpectedly" - - // BadLink indicates a broken link was found in the header array during IngestHeaders loop - BadLink sdk.CodeType = 202 - // BadLinkMessage is the corresponding message - BadLinkMessage = "Headers do not form a consistent chain" + UnexpectedRetargetMessage = "Target changed unexpectedly at block %x" // 300-block AddHeadersWithRetarget @@ -100,32 +95,20 @@ const ( // NotBestKnown means a block should have been the best known, but wasn't NotBestKnown sdk.CodeType = 403 // NotBestKnownMessage is the corresponding message - NotBestKnownMessage = "Provided digest is not current best known" + NotBestKnownMessage = "Provided digest %x is not current best known, expecting block with hash %x" // NotHeaviestAncestor means a later common ancestor was found NotHeaviestAncestor sdk.CodeType = 404 // NotHeaviestAncestorMessage is the corresponding message - NotHeaviestAncestorMessage = "Ancestor must be heaviest common ancestor" + NotHeaviestAncestorMessage = "Ancestor %x is not heaviest common ancestor" // NotHeavier means the proposed new best is not heavier than the current best NotHeavier sdk.CodeType = 405 // NotHeavierMessage is the corresponding message - NotHeavierMessage = "New best hash does not have more work than previous" + NotHeavierMessage = "New best received %x does not have more work than previous best %x" // 500-block Queries - // TODO: Delete the next 2 error codes - - // NotEnoughArguments means there are not enough arguments specified in the path of a query - NotEnoughArguments sdk.CodeType = 501 - // NotEnoughArgumentsMessage is the corresponding message - NotEnoughArgumentsMessage = "Not enough arguments" - - // TooManyArguments means there are too many arguments specified in the path of a query - TooManyArguments sdk.CodeType = 502 - // TooManyArgumentsMessage is the corresponding message - TooManyArgumentsMessage = "Too many arguments" - // MarshalJSON means there was an error marshalling a query result to json MarshalJSON sdk.CodeType = 503 // MarshalJSONMessage is the corresponding message @@ -166,27 +149,27 @@ const ( // RequestPays means the output does not match the pays request RequestPays sdk.CodeType = 607 // RequestPaysMessage is the corresponding message - RequestPaysMessage = "Does not match pays request" + RequestPaysMessage = "Output does not match pays for requestID %d" // RequestValue means the pays value and value of the output does not match RequestValue sdk.CodeType = 608 // RequestValueMessage is the corresponding message - RequestValueMessage = "Does not match value request" + RequestValueMessage = "Output value does not match pays value for requestID %d" // RequestSpends means the request spends does not match the input RequestSpends sdk.CodeType = 609 // RequestSpendsMessage is the corresponding message - RequestSpendsMessage = "Does not match spends request" + RequestSpendsMessage = "Input does not match spends for requestID %d" // NotAncestor means the LCA is not an ancestor of the SPV Proof header NotAncestor sdk.CodeType = 610 // NotAncestorMessage is the corresponding message - NotAncestorMessage = "LCA not ancestor of proof header" + NotAncestorMessage = "LCA %x not ancestor of proof header" // NotEnoughConfs means the proof does not have enough confirmations NotEnoughConfs sdk.CodeType = 611 // NotEnoughConfsMessage is the corresponding message - NotEnoughConfsMessage = "Not enough confirmations" + NotEnoughConfsMessage = "Not enough confirmations for requestID %d" // ActionLength means the pays value is greater than 50 bytes ActionLength sdk.CodeType = 612 @@ -199,39 +182,28 @@ const ( ExternalError sdk.CodeType = 701 ) -// ErrUnknownError throws an error -func ErrUnknownError(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnknownError, UnknownErrorMessage) -} - // ErrBadHeaderLength throws an error -func ErrBadHeaderLength(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHeaderLength, BadHeaderLengthMessage) +func ErrBadHeaderLength(codespace sdk.CodespaceType, label string, digest RawHeader, length int) sdk.Error { + return sdk.NewError(codespace, BadHeaderLength, fmt.Sprint(BadHeaderLengthMessage, label, digest, length)) } -// ErrInsufficientWork throws an error -func ErrInsufficientWork(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, InsufficientWork, InsufficientWorkMessage) +// ErrBadHeight throws an error +func ErrBadHeight(codespace sdk.CodespaceType, label string, digest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, BadHeight, fmt.Sprintf(BadHeightMessage, label, digest)) } -// ErrUnknownBlock throws an error -func ErrUnknownBlock(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnknownBlock, UnknownBlockMessage) +func ErrHeightMismatch(codespace sdk.CodespaceType, prevDigest, digest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, HeightMismatch, fmt.Sprintf(HeightMismatchMessage, prevDigest, digest)) } -// ErrBadHeight throws an error -func ErrBadHeight(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHeight, BadHeightMessage) +// ErrUnknownBlock throws an error +func ErrUnknownBlock(codespace sdk.CodespaceType, label string, digest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, UnknownBlock, fmt.Sprint(UnknownBlockMessage, label, digest)) } // ErrUnexpectedRetarget throws an error -func ErrUnexpectedRetarget(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, UnexpectedRetarget, UnexpectedRetargetMessage) -} - -// ErrBadLink throws an error -func ErrBadLink(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadLink, BadLinkMessage) +func ErrUnexpectedRetarget(codespace sdk.CodespaceType, rawHeader RawHeader) sdk.Error { + return sdk.NewError(codespace, UnexpectedRetarget, fmt.Sprint(UnexpectedRetargetMessage, rawHeader)) } // ErrWrongEnd throws an error @@ -255,28 +227,28 @@ func ErrBadRetarget(codespace sdk.CodespaceType) sdk.Error { } // ErrNotBestKnown throws an error -func ErrNotBestKnown(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotBestKnown, NotBestKnownMessage) +func ErrNotBestKnown(codespace sdk.CodespaceType, invalidBest, expectedBest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, NotBestKnown, fmt.Sprintf(NotBestKnownMessage, invalidBest, expectedBest)) } // ErrNotHeaviestAncestor throws an error -func ErrNotHeaviestAncestor(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotHeaviestAncestor, NotHeaviestAncestorMessage) +func ErrNotHeaviestAncestor(codespace sdk.CodespaceType, ancestor Hash256Digest) sdk.Error { + return sdk.NewError(codespace, NotHeaviestAncestor, fmt.Sprintf(NotHeaviestAncestorMessage, ancestor)) } // ErrNotHeavier throws an error -func ErrNotHeavier(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotHeavier, NotHeavierMessage) +func ErrNotHeavier(codespace sdk.CodespaceType, newBest, prevBest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, NotHeavier, fmt.Sprintf(NotHeavierMessage, newBest, prevBest)) } // ErrBadHash256Digest throws an error -func ErrBadHash256Digest(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHash256Digest, BadHash256DigestMessage) +func ErrBadHash256Digest(codespace sdk.CodespaceType, invalidDigest string) sdk.Error { + return sdk.NewError(codespace, BadHash256Digest, fmt.Sprintf(BadHash256DigestMessage, invalidDigest)) } // ErrBadHex throws an error -func ErrBadHex(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, BadHex, BadHexMessage) +func ErrBadHex(codespace sdk.CodespaceType, invalidHex string) sdk.Error { + return sdk.NewError(codespace, BadHex, fmt.Sprintf(BadHexMessage, invalidHex)) } // ErrBadHexLen throws an error @@ -289,14 +261,9 @@ func ErrAlreadyInit(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, AlreadyInit, AlreadyInitMessage) } -// ErrTooManyArguments throws an error -func ErrTooManyArguments(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, TooManyArguments, TooManyArgumentsMessage) -} - -// ErrNotEnoughArguments throws an error -func ErrNotEnoughArguments(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotEnoughArguments, NotEnoughArgumentsMessage) +// ErrBadOffset throws an error +func ErrBadOffset(codespace sdk.CodespaceType, digest Hash256Digest) sdk.Error { + return sdk.NewError(codespace, BadHexLen, fmt.Sprintf(BadOffsetMessage, digest)) } // ErrMarshalJSON throws an error @@ -345,28 +312,28 @@ func ErrClosedRequest(codespace sdk.CodespaceType) sdk.Error { } // ErrRequestPays throws an error -func ErrRequestPays(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, RequestPays, RequestPaysMessage) +func ErrRequestPays(codespace sdk.CodespaceType, requestID RequestID) sdk.Error { + return sdk.NewError(codespace, RequestPays, fmt.Sprintf(RequestPaysMessage, requestID)) } // ErrRequestValue throws an error -func ErrRequestValue(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, RequestValue, RequestValueMessage) +func ErrRequestValue(codespace sdk.CodespaceType, requestID RequestID) sdk.Error { + return sdk.NewError(codespace, RequestValue, fmt.Sprintf(RequestValueMessage, requestID)) } // ErrRequestSpends throws an error -func ErrRequestSpends(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, RequestSpends, RequestSpendsMessage) +func ErrRequestSpends(codespace sdk.CodespaceType, requestID RequestID) sdk.Error { + return sdk.NewError(codespace, RequestSpends, fmt.Sprintf(RequestSpendsMessage, requestID)) } // ErrNotAncestor throws an error -func ErrNotAncestor(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotAncestor, NotAncestorMessage) +func ErrNotAncestor(codespace sdk.CodespaceType, lca Hash256Digest) sdk.Error { + return sdk.NewError(codespace, NotAncestor, fmt.Sprintf(NotAncestorMessage, lca)) } // ErrNotEnoughConfs throws an error -func ErrNotEnoughConfs(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, NotEnoughConfs, NotEnoughConfsMessage) +func ErrNotEnoughConfs(codespace sdk.CodespaceType, requestID RequestID) sdk.Error { + return sdk.NewError(codespace, NotEnoughConfs, fmt.Sprintf(NotEnoughConfsMessage, requestID)) } // ErrExternal converts any external error into an sdk error diff --git a/golang/x/relay/types/msgs.go b/golang/x/relay/types/msgs.go index 7db38cd4..3f5df761 100644 --- a/golang/x/relay/types/msgs.go +++ b/golang/x/relay/types/msgs.go @@ -131,8 +131,12 @@ func (msg MsgMarkNewHeaviest) Type() string { return "mark_new_heaviest" } // ValidateBasic runs stateless validation func (msg MsgMarkNewHeaviest) ValidateBasic() sdk.Error { - if len(msg.CurrentBest) != 80 || len(msg.NewBest) != 80 { - return ErrBadHeaderLength(DefaultCodespace) + if len(msg.CurrentBest) != 80 { + return ErrBadHeaderLength(DefaultCodespace, "currentBest", msg.CurrentBest, len(msg.CurrentBest)) + } + + if len(msg.NewBest) != 80 { + return ErrBadHeaderLength(DefaultCodespace, "newBest", msg.NewBest, len(msg.NewBest)) } return nil diff --git a/golang/x/relay/types/requests.go b/golang/x/relay/types/requests.go index b64ef81f..5d625e9d 100644 --- a/golang/x/relay/types/requests.go +++ b/golang/x/relay/types/requests.go @@ -45,7 +45,7 @@ func RequestIDFromString(s string) (RequestID, error) { if s[:2] == "0x" { idBytes, err = hex.DecodeString(s[2:]) if err != nil { - return RequestID{}, ErrBadHex(DefaultCodespace) + return RequestID{}, ErrBadHex(DefaultCodespace, s) } } else { id, parseErr := strconv.ParseUint(s, 10, 64) diff --git a/golang/x/relay/types/types.go b/golang/x/relay/types/types.go index 86cdaa60..5904a598 100644 --- a/golang/x/relay/types/types.go +++ b/golang/x/relay/types/types.go @@ -50,7 +50,7 @@ func Hash256DigestFromHex(hexStr string) (Hash256Digest, sdk.Error) { bytes, decodeErr := hex.DecodeString(data) if decodeErr != nil { - return Hash256Digest{}, ErrBadHex(DefaultCodespace) + return Hash256Digest{}, ErrBadHex(DefaultCodespace, hexStr) } digest, newDigestErr := btcspv.NewHash256Digest(bytes) if newDigestErr != nil { diff --git a/testVectors.json b/testVectors.json index 9baee314..2cb02575 100644 --- a/testVectors.json +++ b/testVectors.json @@ -22,7 +22,7 @@ "testCases": [{ "digest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", "offset": 2, - "error": 103, + "error": 104, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "digest": "0x96f25d34d30a1bf4e280d6bb38b228e8993c8b28222d2b000000000000000000", @@ -32,7 +32,7 @@ }, { "digest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", "offset": 3, - "error": 103, + "error": 107, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }] } @@ -438,7 +438,7 @@ }, "internal": false, "isMainnet": true, - "output": 104 + "output": 103 }, { "comment": "header doesn't pass validation, header[0].merkle_root_le is wrong", @@ -832,35 +832,35 @@ "currentBest": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "limit": 20, - "error": 103, + "error": 104, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "currentBest": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "limit": 20, - "error": 103, + "error": 104, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "currentBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "newBest": "0xa4c292016c1585e6f81986f7e216c79d28b15b1d513a10000000000000000000", "limit": 20, - "error": 103, + "error": 104, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "currentBest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", "newBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "limit": 20, - "error": 104, + "error": 102, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", "currentBest": "0xd2406bb15e4f917104d2b2d9320454b947fb08a723301d000000000000000000", "newBest": "0x8bfff9ae28af2aa90adfdb92b218829727886488dcc914000000000000000000", "limit": 20, - "error": 104, + "error": 102, "output": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" }, { "ancestor": "0xfee20039fe494f7408c090e03970ec9b132366066f380d000000000000000000", @@ -906,7 +906,7 @@ "currentBest": "0x00000020d0eb8cc9a5668ddabb83e6f657d940fd56902ceb039f200000000000000000005e407cf51ee81930145648d1e72c1caeafa6f6bfc4a7ec3988bcb7cf69a1594af3fe1d58d2550418e90ec632", "newBest": "9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", "limit": 10, - "error": 103, + "error": 104, "output": "" }, { "bestKnownDigest": "0x58bc010196dd410ab4437e8b1601376e273fa45abb5b3c030000000000000000", From 3eaa33afaa70fe7f80d825c77a2137c62ac1ae23 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Mon, 6 Apr 2020 20:00:37 -0400 Subject: [PATCH 107/148] Set up Dockerfile for deploying relay --- maintainer/Dockerfile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 maintainer/Dockerfile diff --git a/maintainer/Dockerfile b/maintainer/Dockerfile new file mode 100644 index 00000000..e50ecc4a --- /dev/null +++ b/maintainer/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.7 AS build + +WORKDIR /tmp + +RUN pip install pipenv + +COPY Pipfile . +COPY Pipfile.lock . + +RUN pipenv install --system + +COPY maintainer maintainer +COPY setup.py setup.py + +RUN python setup.py install + +ENTRYPOINT ["python", "maintainer/header_forwarder/h.py", ".env"] From 2e5a87b7ad6114726abac47a10559ec2dc5f1562 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 17 Apr 2020 10:41:53 -0400 Subject: [PATCH 108/148] Only auth on bcoin node if auth is available --- maintainer/maintainer/bitcoin/bsock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/bitcoin/bsock.py b/maintainer/maintainer/bitcoin/bsock.py index 8f393591..003bcb98 100644 --- a/maintainer/maintainer/bitcoin/bsock.py +++ b/maintainer/maintainer/bitcoin/bsock.py @@ -29,7 +29,8 @@ async def close_connection() -> None: @sio.event async def connect() -> None: - await sio.call('auth', config.get()['API_KEY']) + if len(config.get()['API_KEY']) > 0: + await sio.call('auth', config.get()['API_KEY']) logger.info(f'connected and authed') From 736fdb4d4c91960b8225912f749129d1341fe66b Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 17 Apr 2020 16:45:50 -0400 Subject: [PATCH 109/148] Fix env file path for maintainer deployment The initcontainer drops the env file in /mnt. --- maintainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/Dockerfile b/maintainer/Dockerfile index e50ecc4a..f00ce831 100644 --- a/maintainer/Dockerfile +++ b/maintainer/Dockerfile @@ -14,4 +14,4 @@ COPY setup.py setup.py RUN python setup.py install -ENTRYPOINT ["python", "maintainer/header_forwarder/h.py", ".env"] +ENTRYPOINT ["python", "maintainer/header_forwarder/h.py", "/mnt/relay-maintainer/.env"] From fa97cb90d8f9c13a03dcea66247a25ada3be115d Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 5 May 2020 14:29:31 +0200 Subject: [PATCH 110/148] Await transaction result Chain transactions should be broadcasted sequentially. Doing this concurrently causes race conditions. --- maintainer/maintainer/ethereum/shared.py | 2 +- maintainer/maintainer/header_forwarder/push.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 2cb7fb25..f02515a0 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -94,7 +94,7 @@ async def sign_and_broadcast( logger.info(f'dispatched transaction {tx_id}') if not ignore_result: - asyncio.ensure_future(_track_tx_result(tx_id)) + await _track_tx_result(tx_id) def make_call_tx( diff --git a/maintainer/maintainer/header_forwarder/push.py b/maintainer/maintainer/header_forwarder/push.py index d2690bd1..0df6866e 100644 --- a/maintainer/maintainer/header_forwarder/push.py +++ b/maintainer/maintainer/header_forwarder/push.py @@ -86,7 +86,7 @@ async def _add_headers(headers: List[RelayHeader]) -> None: method='addHeaders', args=[anchor["raw"], headers_hex], nonce=nonce) - asyncio.create_task(shared.sign_and_broadcast(tx)) + await shared.sign_and_broadcast(tx) async def _add_diff_change(headers: List[RelayHeader]) -> None: @@ -119,7 +119,7 @@ async def _add_diff_change(headers: List[RelayHeader]) -> None: headers_hex], nonce=nonce) - asyncio.create_task(shared.sign_and_broadcast(tx)) + await shared.sign_and_broadcast(tx) async def find_lca( @@ -190,4 +190,4 @@ async def _update_best_digest( f'previous best was {utils.format_header(current_best)}\n' f'new best is {utils.format_header(new_best)}\n') - asyncio.create_task(shared.sign_and_broadcast(tx)) + await shared.sign_and_broadcast(tx) From 2bc583d742a579ef8c4609c9f0da69e3f6747a43 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 5 May 2020 20:03:05 +0200 Subject: [PATCH 111/148] Update genesis blocks Update of genesis blocks to speed up catching up chain's head. --- solidity/migrations/networkInfo.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index 58799953..d030e024 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -6,15 +6,15 @@ const ID_SPACE_SIZE = new BN('2', 10).pow(new BN('32', 10)); const truffleConf = require('../truffle-config'); const bitcoinMain = { - genesis: '0x000040202842774747733a4863b6bbb7b4cfb66baa9287d5ce0d13000000000000000000df550e01d02ee37fce8dd2fbf919a47b8b65684bcb48d4da699078916da2f7decbc7905ebc2013178f58d533', - height: 625332, - epochStart: '0x6e9d58fb0ab8d0181b1c9e54614f80b64004c2e04da310000000000000000000', + genesis: '0x00006020dd02d03c03dbc1f41312a6940e89919ce67fbf99a20307000000000000000000260d70e7ae07c80db07fbf29d09ec1a86d4f788e58098189a6f9021236572a7dd99eb15e397a11178294a823', + height: 629070, + epochStart: '0x459ec50d4ea62a89da04eb1ef3e352ec740bca50e8a808000000000000000000', }; const bitcoinTest = { - genesis: '0x0000ff3ffc663e3a0b12b4cc2c05a425bdaf51922ce090acd8fa3a8a180300000000000084080b23fc40476d284da49fedaea9f7cee3aba33a8bad1347fa54740a29f02752b4c45dfcff031a279c2b3a', - height: 1607272, - epochStart: '0x84a9ec3b82556297ea36d1377901ecaef0bb5a5cf683f9f05103000000000000' + genesis: '0x0000c0205d1103efc13e6647977e3d65f253c3e762451e9ca9b920517d000000000000008442a07bcde3292a888277ea6337ba5bbdfa808ae01535846f19d843144c8f60478bb15e7b41011a88be5d36', + height: 1723030, + epochStart: '0xe2657f702faa9470815005305c45b4be2271c22ade1348e6fe00000000000000' }; module.exports = { From 3ed8800939dce0604e40f39cc8576ac5f3fb8069 Mon Sep 17 00:00:00 2001 From: Sloan Thompson Date: Wed, 13 May 2020 15:17:42 -0400 Subject: [PATCH 112/148] More gas Transactions are stalling on mainnet because we're not juicing them enough. Pumping up the gas price to try and move things along for now. This is temporary, we should at least make this value configurable on the deployment level. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index f02515a0..e583989d 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -14,7 +14,7 @@ GWEI = 1000000000 DEFAULT_GAS = 500_000 -DEFAULT_GAS_PRICE = 2 * GWEI +DEFAULT_GAS_PRICE = 22 * GWEI CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends From bc881b9b503cbdbfdfd8ff3f398fcbc18132f6d9 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Thu, 14 May 2020 19:13:12 -0400 Subject: [PATCH 113/148] Add super-basic gas management to ethereum/shared.py Linear increase based on still-pending transaction when a new one is submitted, and replacing older transactions to the new gas price as well. --- maintainer/maintainer/ethereum/shared.py | 42 +++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index e583989d..ec24692b 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -14,7 +14,8 @@ GWEI = 1000000000 DEFAULT_GAS = 500_000 -DEFAULT_GAS_PRICE = 22 * GWEI +DEFAULT_GAS_PRICE = 10 * GWEI +MAX_GAS_PRICE = 80 * GWEI CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends @@ -94,7 +95,7 @@ async def sign_and_broadcast( logger.info(f'dispatched transaction {tx_id}') if not ignore_result: - await _track_tx_result(tx_id) + asyncio.ensure_future(_track_tx_result(tx, tx_id)) def make_call_tx( @@ -105,7 +106,7 @@ def make_call_tx( nonce: int, value: int = 0, gas: int = DEFAULT_GAS, - gas_price: int = DEFAULT_GAS_PRICE) -> UnsignedEthTx: + gas_price: int = -1) -> UnsignedEthTx: ''' Sends tokens to a recipient Args: @@ -122,6 +123,13 @@ def make_call_tx( logger.debug(f'making tx call {method} on {contract} ' f'with value {value} and {len(args)} args') + # Adjust gas price for current pending txes. + if gas_price == -1: + gas_price = min( + # Let's make real sure there are no zero or negative gas prices, eh? + max(INCOMPLETE_TX_COUNT, 1) * DEFAULT_GAS_PRICE, + MAX_GAS_PRICE) + gas_price = _adjust_gas_price(gas_price) chainId = config.get()['CHAIN_ID'] @@ -159,16 +167,42 @@ def _adjust_gas_price(gas_price: int) -> int: 'very high gas price detected: {} gwei'.format(gas_price / GWEI)) return gas_price +INCOMPLETE_TX_COUNT = 0 + +async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: + global INCOMPLETE_TX_COUNT -async def _track_tx_result(tx_id: str) -> None: '''Keep track of the result of a transaction by polling every 25 seconds''' + INCOMPLETE_TX_COUNT += 1 + lastTxCount = INCOMPLETE_TX_COUNT + receipt_or_none: Optional[Receipt] = None for _ in range(20): await asyncio.sleep(30) receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) if receipt_or_none is not None: + INCOMPLETE_TX_COUNT -= 1 break + else: + if INCOMPLETE_TX_COUNT > lastTxCount: + # If the pending tx count grew, resubmit this transaction with a + # boosted gas cost. Currently that's just a linear multiplier on + # the initial gas. + lastTxCount = INCOMPLETE_TX_COUNT + newTx = UnsignedEthTx( + nonce = tx.nonce, + gasPrice = min(INCOMPLETE_TX_COUNT * tx.gasPrice, MAX_GAS_PRICE), + gas = tx.gas, + to = tx.to, + value = tx.value, + data = tx.data, + chainId = tx.chainId) + + # Bradcast and set up tracking for the new tx, and stop watching + # this one. + sign_and_broadcast(newTx, False) + return if receipt_or_none is None: raise RuntimeError(f'No receipt after 10 minutes: {tx_id}') From 543f2c8a59b3ac62b3cf65e474d9d6332d9a20fa Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 18:30:50 -0400 Subject: [PATCH 114/148] Start INCOMPLETE_TX_COUNT at pending transaction count This assumes the account operating the relay is not doing anything else. --- maintainer/maintainer/ethereum/shared.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index ec24692b..7b8b9973 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -19,7 +19,7 @@ CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends - +INCOMPLETE_TX_COUNT = 0 def _nonce(i: int) -> Iterator[int]: '''Infinite generator for nonces''' @@ -60,6 +60,13 @@ async def init() -> None: NONCE = _nonce(n) logger.info(f'nonce is {n}') + # Start at the pending transaction count, which assumes the account used + # to operate the relay is doing nothing else. + global INCOMPLETE_TX_COUNT + mined_tx_count = int(await CONNECTION._RPC( + method='eth_getTransactionCount', + params=[address, 'latest']), 16) + INCOMPLETE_TX_COUNT = max(n - mined_tx_count, 0) async def close_connection() -> None: try: @@ -167,8 +174,6 @@ def _adjust_gas_price(gas_price: int) -> int: 'very high gas price detected: {} gwei'.format(gas_price / GWEI)) return gas_price -INCOMPLETE_TX_COUNT = 0 - async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: global INCOMPLETE_TX_COUNT From d8bf22f82c5b00c9ccd5203a5ff24eaffa21b3a1 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 18:51:29 -0400 Subject: [PATCH 115/148] Replace pending relay transactions on boot When the relay restarts, it's willing to replace pending transactions with a higher gas price replacement in order to speed up catch-up times. --- maintainer/maintainer/ethereum/shared.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 7b8b9973..80c43890 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -56,17 +56,27 @@ async def init() -> None: else: global NONCE address = cast(str, c['ETH_ADDRESS']) - n = await CONNECTION.get_nonce(address) - NONCE = _nonce(n) - logger.info(f'nonce is {n}') + # Get the current nonce = total txes including pending. + all_tx_count = await CONNECTION.get_nonce(address) - # Start at the pending transaction count, which assumes the account used - # to operate the relay is doing nothing else. - global INCOMPLETE_TX_COUNT + # Get the already-mined count. mined_tx_count = int(await CONNECTION._RPC( method='eth_getTransactionCount', params=[address, 'latest']), 16) - INCOMPLETE_TX_COUNT = max(n - mined_tx_count, 0) + + pending_tx_count = max(all_tx_count - mined_tx_count, 0) + + # Treat all pending txes as incomplete. + global INCOMPLETE_TX_COUNT + INCOMPLETE_TX_COUNT = pending_tx_count + + # Replace all pending txes by starting the nonce at the mined count. + # Note that we could crash if the next tx we send finds the first + # unconfirmed nonce having already been mined---this is fine, the + # process can be restarted and will read the latest pending and mined + # state at that time. + NONCE = _nonce(mined_tx_count) + logger.info(f'nonce is {mined_tx_count}') async def close_connection() -> None: try: From afa04b97b97e30b558a9c065d5ec12d1ed94b085 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:16:04 -0400 Subject: [PATCH 116/148] Track completed/pending nonce to use for gas boost The adjustment is now based on the difference between any given transaction's nonce and the next one up. This helps transactions that have been waiting for longer get a boosted price, while more recent transactions wait longer if needed. It also ensures the price drops back down to the default price when the default price becomes reasonable again. --- maintainer/maintainer/ethereum/shared.py | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 80c43890..f6758b19 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -19,7 +19,8 @@ CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends -INCOMPLETE_TX_COUNT = 0 +LATEST_PENDING_NONCE = 0 +LATEST_COMPLETE_NONCE = 0 def _nonce(i: int) -> Iterator[int]: '''Infinite generator for nonces''' @@ -55,28 +56,24 @@ async def init() -> None: 'No ethereum privkey found in env config. Txns will error') else: global NONCE + global LATEST_PENDING_NONCE + global LATEST_COMPLETE_NONCE address = cast(str, c['ETH_ADDRESS']) - # Get the current nonce = total txes including pending. - all_tx_count = await CONNECTION.get_nonce(address) - # Get the already-mined count. mined_tx_count = int(await CONNECTION._RPC( method='eth_getTransactionCount', params=[address, 'latest']), 16) - pending_tx_count = max(all_tx_count - mined_tx_count, 0) - - # Treat all pending txes as incomplete. - global INCOMPLETE_TX_COUNT - INCOMPLETE_TX_COUNT = pending_tx_count + LATEST_PENDING_NONCE = await CONNECTION.get_nonce(address) - 1 + LATEST_COMPLETE_NONCE = mined_tx_count # Replace all pending txes by starting the nonce at the mined count. # Note that we could crash if the next tx we send finds the first # unconfirmed nonce having already been mined---this is fine, the # process can be restarted and will read the latest pending and mined # state at that time. - NONCE = _nonce(mined_tx_count) - logger.info(f'nonce is {mined_tx_count}') + NONCE = _nonce(LATEST_COMPLETE_NONCE + 1) + logger.info(f'nonce is {LATEST_COMPLETE_NONCE + 1}') async def close_connection() -> None: try: @@ -140,13 +137,18 @@ def make_call_tx( logger.debug(f'making tx call {method} on {contract} ' f'with value {value} and {len(args)} args') + global LATEST_PENDING_NONCE + # Adjust gas price for current pending txes. if gas_price == -1: gas_price = min( # Let's make real sure there are no zero or negative gas prices, eh? - max(INCOMPLETE_TX_COUNT, 1) * DEFAULT_GAS_PRICE, + max(nonce - LATEST_PENDING_NONCE, 1) * DEFAULT_GAS_PRICE, MAX_GAS_PRICE) + if nonce > LATEST_PENDING_NONCE: + LATEST_PENDING_NONCE = nonce + gas_price = _adjust_gas_price(gas_price) chainId = config.get()['CHAIN_ID'] @@ -185,29 +187,28 @@ def _adjust_gas_price(gas_price: int) -> int: return gas_price async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: - global INCOMPLETE_TX_COUNT - '''Keep track of the result of a transaction by polling every 25 seconds''' - INCOMPLETE_TX_COUNT += 1 - lastTxCount = INCOMPLETE_TX_COUNT - receipt_or_none: Optional[Receipt] = None + global LATEST_PENDING_NONCE + global LATEST_COMPLETE_NONCE + for _ in range(20): await asyncio.sleep(30) receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) if receipt_or_none is not None: - INCOMPLETE_TX_COUNT -= 1 + if tx.nonce > LATEST_COMPLETE_NONCE: + LATEST_COMPLETE_NONCE = tx.nonce break else: - if INCOMPLETE_TX_COUNT > lastTxCount: + if LATEST_PENDING_NONCE > tx.nonce: # If the pending tx count grew, resubmit this transaction with a # boosted gas cost. Currently that's just a linear multiplier on # the initial gas. - lastTxCount = INCOMPLETE_TX_COUNT + pendingTxCount = max(LATEST_PENDING_NONCE - tx.nonce, 1) newTx = UnsignedEthTx( nonce = tx.nonce, - gasPrice = min(INCOMPLETE_TX_COUNT * tx.gasPrice, MAX_GAS_PRICE), + gasPrice = min(pendingTxCount * DEFAULT_GAS_PRICE, MAX_GAS_PRICE), gas = tx.gas, to = tx.to, value = tx.value, From 56c74999a0d66d5752f380ee7cdc2247f905b2c6 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:26:40 -0400 Subject: [PATCH 117/148] Properly compute initial gas price When there are other pending transactions, the gas price was always being set to 1 initially. This fixes that, setting it to a high value if there are many pending transactions ahead. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index f6758b19..d78d71da 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -143,7 +143,7 @@ def make_call_tx( if gas_price == -1: gas_price = min( # Let's make real sure there are no zero or negative gas prices, eh? - max(nonce - LATEST_PENDING_NONCE, 1) * DEFAULT_GAS_PRICE, + max(LATEST_PENDING_NONCE - n, 1) * DEFAULT_GAS_PRICE, MAX_GAS_PRICE) if nonce > LATEST_PENDING_NONCE: From 0a08522ab94f76a971bf285a3408a2428a5f7bbc Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:27:16 -0400 Subject: [PATCH 118/148] Add some nonce/pending transaction info logging --- maintainer/maintainer/ethereum/shared.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index d78d71da..d4cdd9fc 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -66,6 +66,7 @@ async def init() -> None: LATEST_PENDING_NONCE = await CONNECTION.get_nonce(address) - 1 LATEST_COMPLETE_NONCE = mined_tx_count + logger.info(f'latest pending nonce is {LATEST_PENDING_NONCE}') # Replace all pending txes by starting the nonce at the mined count. # Note that we could crash if the next tx we send finds the first @@ -73,7 +74,7 @@ async def init() -> None: # process can be restarted and will read the latest pending and mined # state at that time. NONCE = _nonce(LATEST_COMPLETE_NONCE + 1) - logger.info(f'nonce is {LATEST_COMPLETE_NONCE + 1}') + logger.info(f'next nonce is {LATEST_COMPLETE_NONCE + 1}') async def close_connection() -> None: try: @@ -206,9 +207,11 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: # boosted gas cost. Currently that's just a linear multiplier on # the initial gas. pendingTxCount = max(LATEST_PENDING_NONCE - tx.nonce, 1) + newGasPrice = min(pendingTxCount * DEFAULT_GAS_PRICE, MAX_GAS_PRICE) + logger.info(f'resubmitting {tx_id} with gas price {newGasPrice}') newTx = UnsignedEthTx( nonce = tx.nonce, - gasPrice = min(pendingTxCount * DEFAULT_GAS_PRICE, MAX_GAS_PRICE), + gasPrice = newGasPrice, gas = tx.gas, to = tx.to, value = tx.value, From b966feb5c9d658d5c2fe79a493d00c5d31c07e78 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:30:52 -0400 Subject: [PATCH 119/148] n is not a nonce But it is a variable we used to know. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index d4cdd9fc..a0f9d6f2 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -144,7 +144,7 @@ def make_call_tx( if gas_price == -1: gas_price = min( # Let's make real sure there are no zero or negative gas prices, eh? - max(LATEST_PENDING_NONCE - n, 1) * DEFAULT_GAS_PRICE, + max(LATEST_PENDING_NONCE - nonce, 1) * DEFAULT_GAS_PRICE, MAX_GAS_PRICE) if nonce > LATEST_PENDING_NONCE: From 03b371e5ad8e9cd6a6c7c0281fabc210678a25c6 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:35:12 -0400 Subject: [PATCH 120/148] Fix an off-by-one for starting nonce --- maintainer/maintainer/ethereum/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index a0f9d6f2..c0ff7a82 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -73,8 +73,8 @@ async def init() -> None: # unconfirmed nonce having already been mined---this is fine, the # process can be restarted and will read the latest pending and mined # state at that time. - NONCE = _nonce(LATEST_COMPLETE_NONCE + 1) - logger.info(f'next nonce is {LATEST_COMPLETE_NONCE + 1}') + NONCE = _nonce(LATEST_COMPLETE_NONCE) + logger.info(f'next nonce is {LATEST_COMPLETE_NONCE}') async def close_connection() -> None: try: From e7e5f05866013d64d6b9294159d16506376431c3 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:35:21 -0400 Subject: [PATCH 121/148] await sign_and_broadcast in _track_tx_result Strictly speaking it doesn't make a huge difference, but it handles a Python warning. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index c0ff7a82..54ebf2bb 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -220,7 +220,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: # Bradcast and set up tracking for the new tx, and stop watching # this one. - sign_and_broadcast(newTx, False) + await sign_and_broadcast(newTx, False) return if receipt_or_none is None: From bae36475b0aa650dc8499968fd77746a13e2c7d6 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:46:40 -0400 Subject: [PATCH 122/148] Don't try catching up nonces if already caught up --- maintainer/maintainer/ethereum/shared.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 54ebf2bb..13d862b9 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -73,7 +73,13 @@ async def init() -> None: # unconfirmed nonce having already been mined---this is fine, the # process can be restarted and will read the latest pending and mined # state at that time. - NONCE = _nonce(LATEST_COMPLETE_NONCE) + # + # If all pending nonces are already complete, make sure to start 1 + # ahead. + next_nonce = LATEST_COMPLETE_NONCE + if LATEST_COMPLETE_NONCE == LATEST_PENDING_NONCE: + next_nonce += 1 + NONCE = _nonce(next_nonce) logger.info(f'next nonce is {LATEST_COMPLETE_NONCE}') async def close_connection() -> None: From 60e2c8b2ee15352547cdfd08c95de17c24f441d9 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 19:55:14 -0400 Subject: [PATCH 123/148] Boost gas price even if a transaction waits alone Ideally the relay doesn't pile up unconfirmed transactions at all. Count ticks so that, as a transaction sits for longer, it is replaced by gradually increased gas costs. Also update default gas price to 20, which is more reasonable on mainnet, and adjust the curve to increase by 20% increments instead of by full DEFAULT_GAS_PRICE increments. --- maintainer/maintainer/ethereum/shared.py | 36 +++++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 13d862b9..305dc813 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -14,7 +14,7 @@ GWEI = 1000000000 DEFAULT_GAS = 500_000 -DEFAULT_GAS_PRICE = 10 * GWEI +DEFAULT_GAS_PRICE = 20 * GWEI MAX_GAS_PRICE = 80 * GWEI CONNECTION: ethrpc.BaseRPC @@ -148,10 +148,7 @@ def make_call_tx( # Adjust gas price for current pending txes. if gas_price == -1: - gas_price = min( - # Let's make real sure there are no zero or negative gas prices, eh? - max(LATEST_PENDING_NONCE - nonce, 1) * DEFAULT_GAS_PRICE, - MAX_GAS_PRICE) + gas_price = _compute_tx_gas_price(nonce, 0) if nonce > LATEST_PENDING_NONCE: LATEST_PENDING_NONCE = nonce @@ -193,13 +190,24 @@ def _adjust_gas_price(gas_price: int) -> int: 'very high gas price detected: {} gwei'.format(gas_price / GWEI)) return gas_price +def _compute_tx_gas_price(tx_nonce, tx_ticks): + '''Compute the proper gas price, adjusting for other pending txes and how + long this tx has been pending, taking the max gas price into account.''' + gas_price_factor = max(LATEST_PENDING_NONCE - tx_nonce + tx_ticks, 0) + adjusted_gas_price = (1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE + + return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) + async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: '''Keep track of the result of a transaction by polling every 25 seconds''' receipt_or_none: Optional[Receipt] = None - global LATEST_PENDING_NONCE global LATEST_COMPLETE_NONCE + # Number of ticks since submission occurred without a receipt. + ticks = 0 + latest_gas_price = tx.gas_price + for _ in range(20): await asyncio.sleep(30) receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) @@ -208,16 +216,16 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: LATEST_COMPLETE_NONCE = tx.nonce break else: - if LATEST_PENDING_NONCE > tx.nonce: - # If the pending tx count grew, resubmit this transaction with a - # boosted gas cost. Currently that's just a linear multiplier on - # the initial gas. - pendingTxCount = max(LATEST_PENDING_NONCE - tx.nonce, 1) - newGasPrice = min(pendingTxCount * DEFAULT_GAS_PRICE, MAX_GAS_PRICE) - logger.info(f'resubmitting {tx_id} with gas price {newGasPrice}') + ticks += 1 + new_gas_price = _compute_tx_gas_price(tx.nonce, ticks) + + # If the new gas price is higher, resubmit this transaction with a + # boosted gas price to improve chances of confirmation. + if new_gas_price > latest_gas_price: + logger.info(f'resubmitting {tx_id} with gas price {new_gas_price}') newTx = UnsignedEthTx( nonce = tx.nonce, - gasPrice = newGasPrice, + gasPrice = new_gas_price, gas = tx.gas, to = tx.to, value = tx.value, From a5b03eac1ec35297b84ef9b479e0b0658d103bb7 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 20:05:03 -0400 Subject: [PATCH 124/148] Drop LATEST_COMPLETED_NONCE global It's no longer needed, as the transaction nonce of the transaction under consideration is the one used to make decisions about gas price, rather than the overall latest completed transaction. --- maintainer/maintainer/ethereum/shared.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 305dc813..d7e65099 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -20,7 +20,6 @@ CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends LATEST_PENDING_NONCE = 0 -LATEST_COMPLETE_NONCE = 0 def _nonce(i: int) -> Iterator[int]: '''Infinite generator for nonces''' @@ -57,7 +56,6 @@ async def init() -> None: else: global NONCE global LATEST_PENDING_NONCE - global LATEST_COMPLETE_NONCE address = cast(str, c['ETH_ADDRESS']) # Get the already-mined count. mined_tx_count = int(await CONNECTION._RPC( @@ -65,7 +63,6 @@ async def init() -> None: params=[address, 'latest']), 16) LATEST_PENDING_NONCE = await CONNECTION.get_nonce(address) - 1 - LATEST_COMPLETE_NONCE = mined_tx_count logger.info(f'latest pending nonce is {LATEST_PENDING_NONCE}') # Replace all pending txes by starting the nonce at the mined count. @@ -76,11 +73,11 @@ async def init() -> None: # # If all pending nonces are already complete, make sure to start 1 # ahead. - next_nonce = LATEST_COMPLETE_NONCE - if LATEST_COMPLETE_NONCE == LATEST_PENDING_NONCE: + next_nonce = mined_tx_count + if mined_tx_count == LATEST_PENDING_NONCE: next_nonce += 1 NONCE = _nonce(next_nonce) - logger.info(f'next nonce is {LATEST_COMPLETE_NONCE}') + logger.info(f'next nonce is {next_nonce}') async def close_connection() -> None: try: @@ -202,8 +199,6 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: '''Keep track of the result of a transaction by polling every 25 seconds''' receipt_or_none: Optional[Receipt] = None - global LATEST_COMPLETE_NONCE - # Number of ticks since submission occurred without a receipt. ticks = 0 latest_gas_price = tx.gas_price @@ -212,8 +207,6 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: await asyncio.sleep(30) receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) if receipt_or_none is not None: - if tx.nonce > LATEST_COMPLETE_NONCE: - LATEST_COMPLETE_NONCE = tx.nonce break else: ticks += 1 From e61f16dfa3d9fbca3c409dd7a85d014227475827 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 21:34:25 -0400 Subject: [PATCH 125/148] Ensure adjusted_gas_price is an integer --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index d7e65099..6cc173bd 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -191,7 +191,7 @@ def _compute_tx_gas_price(tx_nonce, tx_ticks): '''Compute the proper gas price, adjusting for other pending txes and how long this tx has been pending, taking the max gas price into account.''' gas_price_factor = max(LATEST_PENDING_NONCE - tx_nonce + tx_ticks, 0) - adjusted_gas_price = (1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE + adjusted_gas_price = round((1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE) return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) From 49ff5f08e5b2f0e3fb72a3bb7e51b9a3dfda50b7 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 21:38:24 -0400 Subject: [PATCH 126/148] Fix a comment typo, Brad does not cast --- maintainer/maintainer/ethereum/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 6cc173bd..4e8efa45 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -225,8 +225,8 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: data = tx.data, chainId = tx.chainId) - # Bradcast and set up tracking for the new tx, and stop watching - # this one. + # Broadcast and set up tracking for the new tx, and stop + # watching this one. await sign_and_broadcast(newTx, False) return From 1d7a288614b5e8b909978b8b5272e94b80ca26e2 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 21:44:51 -0400 Subject: [PATCH 127/148] Fix a bad reference to UnsignedTx.gasPrice --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 4e8efa45..79b6ef4c 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -201,7 +201,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: # Number of ticks since submission occurred without a receipt. ticks = 0 - latest_gas_price = tx.gas_price + latest_gas_price = tx.gasPrice for _ in range(20): await asyncio.sleep(30) From 03831dece596f5978f029b4fae7275b8e2717f96 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 20 May 2020 22:47:14 -0400 Subject: [PATCH 128/148] Close the loop for ongong gas price increases tick information is passed along with each new transaction submission, and tx results are tracked all the way down. Finally, "known transaction" errors are caught and retried with a higher gas price after a wait. Still missing: handling _underpriced_ transactions. --- maintainer/maintainer/ethereum/shared.py | 40 ++++++++++++++---------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 79b6ef4c..7440abef 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -89,7 +89,8 @@ async def close_connection() -> None: async def sign_and_broadcast( tx: UnsignedEthTx, - ignore_result: bool = False) -> None: + ignore_result: bool = False, + ticks: int = 0) -> None: '''Sign an ethereum transaction and broadcast it to the network''' c = config.get() privkey = c['PRIVKEY'] @@ -99,21 +100,28 @@ async def sign_and_broadcast( if privkey is None and unlock_code is None: raise RuntimeError('Attempted to sign tx without access to key') - if privkey is None: - logger.debug('signing with ether node') - await CONNECTION._RPC( - 'personal_unlockAccount', - [address, unlock_code]) - tx_id = await CONNECTION.send_transaction(cast(str, address), tx) - else: - logger.debug('signing with local key') - signed = tx.sign(cast(bytes, privkey)) - serialized = signed.serialize_hex() - tx_id = await CONNECTION.broadcast(serialized) + try: + if privkey is None: + logger.debug('signing with ether node') + await CONNECTION._RPC( + 'personal_unlockAccount', + [address, unlock_code]) + tx_id = await CONNECTION.send_transaction(cast(str, address), tx) + else: + logger.debug('signing with local key') + signed = tx.sign(cast(bytes, privkey)) + serialized = signed.serialize_hex() + tx_id = await CONNECTION.broadcast(serialized) + except RuntimeError as err: + if type(err.args[0]) is dict and 'known transaction: ' in dict(err.args[0])['message']: + tx_id = dict(err.args[0])[19:] + else: + raise err # re-raise + logger.info(f'dispatched transaction {tx_id}') if not ignore_result: - asyncio.ensure_future(_track_tx_result(tx, tx_id)) + asyncio.ensure_future(_track_tx_result(tx, tx_id, ticks)) def make_call_tx( @@ -195,12 +203,10 @@ def _compute_tx_gas_price(tx_nonce, tx_ticks): return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) -async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: +async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> None: '''Keep track of the result of a transaction by polling every 25 seconds''' receipt_or_none: Optional[Receipt] = None - # Number of ticks since submission occurred without a receipt. - ticks = 0 latest_gas_price = tx.gasPrice for _ in range(20): @@ -227,7 +233,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str) -> None: # Broadcast and set up tracking for the new tx, and stop # watching this one. - await sign_and_broadcast(newTx, False) + await sign_and_broadcast(newTx, True, ticks) return if receipt_or_none is None: From 57095cd68976c409172acba2d21ea93f576ffedf Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 29 Jul 2020 17:12:49 -0400 Subject: [PATCH 129/148] Add additional nonce/gas price logging In cases where a transaction fails to go through, some logging clarifies the nonce and gas price information. --- maintainer/maintainer/ethereum/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 7440abef..4e5a544c 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -100,6 +100,7 @@ async def sign_and_broadcast( if privkey is None and unlock_code is None: raise RuntimeError('Attempted to sign tx without access to key') + logger.info(f'dispatching transaction at nonce {tx.nonce} with gas price {tx.gasPrice}') try: if privkey is None: logger.debug('signing with ether node') @@ -118,8 +119,7 @@ async def sign_and_broadcast( else: raise err # re-raise - - logger.info(f'dispatched transaction {tx_id}') + logger.info(f'dispatched transaction {tx_id} with gas price {tx.gasPrice}') if not ignore_result: asyncio.ensure_future(_track_tx_result(tx, tx_id, ticks)) From 419b572cc6277e266b7e901ffc6a07da5705a47f Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 29 Jul 2020 17:15:40 -0400 Subject: [PATCH 130/148] Underpriced transactions go down the gas-bump path Since underpriced transactions indicate the submitted transaction for that nonce has too low a gas price, entering the gas-bump loop allows the gas bumping to get the transaction to an appropriate price. To do this, _track_tx_result can now operate without a tx_id. This is because, in the case of underpriced transactions, no tx_id is available to pass along. A lack of tx_id is treated as a transaction needing retry. --- maintainer/maintainer/ethereum/shared.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 4e5a544c..004f5128 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -116,6 +116,10 @@ async def sign_and_broadcast( except RuntimeError as err: if type(err.args[0]) is dict and 'known transaction: ' in dict(err.args[0])['message']: tx_id = dict(err.args[0])[19:] + elif 'transaction underpriced' in err.args[0]: + # Move on to a retry + asyncio.ensure_future(_track_tx_result(tx, "")) + return else: raise err # re-raise @@ -211,7 +215,10 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non for _ in range(20): await asyncio.sleep(30) - receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) + receipt_or_none = None + if tx_id != "": + receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) + if receipt_or_none is not None: break else: From 49371c76cb193d2d1c806f20c0c3281f06fc1506 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 29 Jul 2020 17:16:26 -0400 Subject: [PATCH 131/148] Track gas-bumped transactions Resubmissions with higher gas prices were not being tracked, so only one gas bump could occur. These are now properly tracked for success, so that they can have their gas price bumped if they don't succeed in a reasonable amount of time. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 004f5128..20f3abcf 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -240,7 +240,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non # Broadcast and set up tracking for the new tx, and stop # watching this one. - await sign_and_broadcast(newTx, True, ticks) + await sign_and_broadcast(newTx, False, ticks) return if receipt_or_none is None: From 019273a513f8f1ea6a325b96818c4bd43c453424 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Wed, 29 Jul 2020 17:17:14 -0400 Subject: [PATCH 132/148] Don't crash if an LCA isn't found in 5 attempts Instead, wait 60 seconds and then try again. This is normal when gas prices require multiple bumps to clear the related transactions. --- maintainer/maintainer/header_forwarder/push.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/header_forwarder/push.py b/maintainer/maintainer/header_forwarder/push.py index 0df6866e..8fe0742a 100644 --- a/maintainer/maintainer/header_forwarder/push.py +++ b/maintainer/maintainer/header_forwarder/push.py @@ -144,6 +144,7 @@ async def find_lca( RelayHeader, await bcoin_rpc.get_header_by_hash_le(ancestor['prevhash'])) await asyncio.sleep(15) + raise RuntimeError('Unable to find LCA after 5 attempts') @@ -161,7 +162,17 @@ async def _update_best_digest( RelayHeader, await bcoin_rpc.get_header_by_hash_le(current_best_digest)) - ancestor = await find_lca(new_best, current_best) + try: + ancestor = await find_lca(new_best, current_best) + except RuntimeError as err: + if err.args[0] == 'Unable to find LCA after 5 attempts': + logger.warn(err) + # Retry after a while, we're probably behind. + await asyncio.sleep(60) + continue + else: + raise err # re-raise + delta = new_best['height'] - ancestor['height'] + 1 tx = shared.make_call_tx( From 5b5e4065201ee86f4278c32e9ecae1bbaccc452e Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Thu, 30 Jul 2020 08:12:59 -0400 Subject: [PATCH 133/148] Log mined tx count at startup --- maintainer/maintainer/ethereum/shared.py | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 20f3abcf..5b8422e0 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -61,6 +61,7 @@ async def init() -> None: mined_tx_count = int(await CONNECTION._RPC( method='eth_getTransactionCount', params=[address, 'latest']), 16) + logger.info(f'mined tx count is {mined_tx_count}') LATEST_PENDING_NONCE = await CONNECTION.get_nonce(address) - 1 logger.info(f'latest pending nonce is {LATEST_PENDING_NONCE}') From 578afea302a2927a0d617640462ef77ae0e94465 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Thu, 30 Jul 2020 08:21:25 -0400 Subject: [PATCH 134/148] Aggressively clear unmined txes during gas bumping Before, there was an equivalent gas price bump for remaining unmined for 30 seconds and for having additional transactions stuck behind it. Now, having additional transactions stuck behind a transaction gives it a much higher gas bump (2^n instead of just n), ensuring that transactions that are blocking the contract clear quickly. --- maintainer/maintainer/ethereum/shared.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 5b8422e0..89be646a 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -203,7 +203,7 @@ def _adjust_gas_price(gas_price: int) -> int: def _compute_tx_gas_price(tx_nonce, tx_ticks): '''Compute the proper gas price, adjusting for other pending txes and how long this tx has been pending, taking the max gas price into account.''' - gas_price_factor = max(LATEST_PENDING_NONCE - tx_nonce + tx_ticks, 0) + gas_price_factor = max(2**(LATEST_PENDING_NONCE - tx_nonce) + tx_ticks, 0) adjusted_gas_price = round((1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE) return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) @@ -244,6 +244,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non await sign_and_broadcast(newTx, False, ticks) return + # This is reachable only when we've hit max gas. if receipt_or_none is None: raise RuntimeError(f'No receipt after 10 minutes: {tx_id}') From 780cdaafb6bac02e523273a8aa56229dbd9bf500 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:03:00 -0400 Subject: [PATCH 135/148] Fix mined_tx_count This number was actually mined_tx_count + 1, apparently. This also lets us drop an if statement, simplifying code flow. --- maintainer/maintainer/ethereum/shared.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 89be646a..ef18ee60 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -60,7 +60,7 @@ async def init() -> None: # Get the already-mined count. mined_tx_count = int(await CONNECTION._RPC( method='eth_getTransactionCount', - params=[address, 'latest']), 16) + params=[address, 'latest']), 16) - 1 logger.info(f'mined tx count is {mined_tx_count}') LATEST_PENDING_NONCE = await CONNECTION.get_nonce(address) - 1 @@ -74,9 +74,7 @@ async def init() -> None: # # If all pending nonces are already complete, make sure to start 1 # ahead. - next_nonce = mined_tx_count - if mined_tx_count == LATEST_PENDING_NONCE: - next_nonce += 1 + next_nonce = mined_tx_count + 1 NONCE = _nonce(next_nonce) logger.info(f'next nonce is {next_nonce}') From 8378b914e4b90a7f19421ac90f42e42cd5e62b27 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:03:52 -0400 Subject: [PATCH 136/148] Handle already known errors like transaction underpriced errors Additionally, this scenario is logged at WARN level so there's a trace that the maintainer is boosting gas in this case. --- maintainer/maintainer/ethereum/shared.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index ef18ee60..14b3df5f 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -115,8 +115,14 @@ async def sign_and_broadcast( except RuntimeError as err: if type(err.args[0]) is dict and 'known transaction: ' in dict(err.args[0])['message']: tx_id = dict(err.args[0])[19:] - elif 'transaction underpriced' in err.args[0]: - # Move on to a retry + elif 'transaction underpriced' in err.args[0] or 'already known' in err.args[0]: + logger.warn( + f'Got an error {err} submitting nonce {tx.nonce} at gas price ' + + f'{tx.gasPrice}; boosting gas.' + ) + # We're trying to submit a transaction that's already been + # submitted; start a retry loop so we can climb to a higher + # gas level if needed. asyncio.ensure_future(_track_tx_result(tx, "")) return else: From 46712249cd9d04728dd7a904478cb39369c97f24 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:04:33 -0400 Subject: [PATCH 137/148] Do not crash on nonce too low errors This typically happens because a lower-priced version of the same transaction cleared while a higher-priced version was about to be submitted. Rather than bomb out, the transaction submission is simply skipped in that case. --- maintainer/maintainer/ethereum/shared.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 14b3df5f..396fa18c 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -125,6 +125,12 @@ async def sign_and_broadcast( # gas level if needed. asyncio.ensure_future(_track_tx_result(tx, "")) return + elif 'nonce too low' in err.args[0] and ticks > 0: + logger.warn( + f'Got an error {err} submitting nonce {tx.nonce} at gas price ' + + f'{tx.gasPrice}; assuming a lower-priced version cleared and ' + + f'continuing normally.' + ) else: raise err # re-raise From 1fbf2e56399395f33450031bad1effb49cd8afcf Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:04:52 -0400 Subject: [PATCH 138/148] Include nonce + gas price info in a few logs This makes for easier correlation of logs during as boosts when multiple transactions are in the process of retrying. --- maintainer/maintainer/ethereum/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 396fa18c..af4c3ea6 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -134,7 +134,7 @@ async def sign_and_broadcast( else: raise err # re-raise - logger.info(f'dispatched transaction {tx_id} with gas price {tx.gasPrice}') + logger.info(f'dispatched transaction {tx_id} at nonce {tx.nonce} with gas price {tx.gasPrice}') if not ignore_result: asyncio.ensure_future(_track_tx_result(tx, tx_id, ticks)) @@ -256,7 +256,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non # This is reachable only when we've hit max gas. if receipt_or_none is None: - raise RuntimeError(f'No receipt after 10 minutes: {tx_id}') + raise RuntimeError(f'No receipt after 10 minutes: {tx_id}, nonce: {tx.nonce}, gas price: {tx.gasPrice}') receipt = cast(Receipt, receipt_or_none) logger.info(f'Receipt for {tx_id} status is {receipt["status"]}') From dfeb45f3ae7f55a4742005fae750123dbbd6eafc Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:05:37 -0400 Subject: [PATCH 139/148] Gas price factor is now (txes ahead) * ticks This makes a new transaction blocked ahead of this one boost the impact of the number of ticks this transaction has been waiting. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index af4c3ea6..4cd1df1c 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -213,7 +213,7 @@ def _adjust_gas_price(gas_price: int) -> int: def _compute_tx_gas_price(tx_nonce, tx_ticks): '''Compute the proper gas price, adjusting for other pending txes and how long this tx has been pending, taking the max gas price into account.''' - gas_price_factor = max(2**(LATEST_PENDING_NONCE - tx_nonce) + tx_ticks, 0) + gas_price_factor = max((LATEST_PENDING_NONCE - tx_nonce + 1) * tx_ticks, 0) adjusted_gas_price = round((1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE) return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) From 55fd491222a13464f1208d35c05c643ead01c630 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:06:41 -0400 Subject: [PATCH 140/148] When catching up to existing txes, don't wait 30s Before, boosting gas would wait 30s at each tick, even if there was already a tx at this nonce and this or higher gas price. This wait is now gone, so if there's a catch-up to be done it happens in a tight loop. --- maintainer/maintainer/ethereum/shared.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 4cd1df1c..92da4380 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -225,9 +225,13 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non latest_gas_price = tx.gasPrice for _ in range(20): - await asyncio.sleep(30) receipt_or_none = None + + # For a blank tx_id, skip the sleep and go straight to cranking gas. + # Blank tx_ids are really just us trying to catch up to the latest used + # gas price. if tx_id != "": + await asyncio.sleep(30) receipt_or_none = await CONNECTION.get_tx_receipt(tx_id) if receipt_or_none is not None: From 1895bdf5f43dc1570b911806368b76a8b16ab1b0 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:07:22 -0400 Subject: [PATCH 141/148] Switch an await to an ensure_future This shouldn't have much of an effect in practice, but is a clearer expression of the intent here. --- maintainer/maintainer/ethereum/shared.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 92da4380..cb2df295 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -254,8 +254,7 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non chainId = tx.chainId) # Broadcast and set up tracking for the new tx, and stop - # watching this one. - await sign_and_broadcast(newTx, False, ticks) + asyncio.ensure_future(sign_and_broadcast(newTx, False, ticks)) return # This is reachable only when we've hit max gas. From b1333498dbf7707119f653ffbc56a04f452efaf6 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:10:43 -0400 Subject: [PATCH 142/148] Add logging and retries when boosting gas to match existing tx When the maintainer restarts, its transaction resubmissions start at 1 tick and have to catch up to the number of ticks of the existing pending transactions on chain, if any. In these cases, the new gas price can end up below the latest gas price. For these situations, the gas price is now boosted until it either reaches MAX_GAS_PRICE or exceeds the latest gas price. Additionally, irrespective of how the gas bump path is handled, the path through it is logged. --- maintainer/maintainer/ethereum/shared.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index cb2df295..68edadce 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -256,6 +256,14 @@ async def _track_tx_result(tx: UnsignedEthTx, tx_id: str, ticks: int = 0) -> Non # Broadcast and set up tracking for the new tx, and stop asyncio.ensure_future(sign_and_broadcast(newTx, False, ticks)) return + # If gas is still below the max gas price, try again with the next + # tick. + elif new_gas_price < MAX_GAS_PRICE: + logger.info(f'attempting to boost gas for {tx_id} at gas price {latest_gas_price} past gas price {new_gas_price}, tick {ticks}') + asyncio.ensure_future(_track_tx_result(tx, tx_id, ticks)) + return + else: + logger.info(f'no gas price bump possible, continuing to wait for receipt: {tx_id}, nonce: {tx.nonce}, gas price: {tx.gasPrice}, bumped price: {new_gas_price}') # This is reachable only when we've hit max gas. if receipt_or_none is None: From d2d39e1b243304bbbeb0e98ba7135e6d51d0bd09 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Fri, 31 Jul 2020 17:10:58 -0400 Subject: [PATCH 143/148] Set max gas to 120 gwei Unfortunately, this is a necessity in the current gas price climate. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 68edadce..cd7f9867 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -15,7 +15,7 @@ GWEI = 1000000000 DEFAULT_GAS = 500_000 DEFAULT_GAS_PRICE = 20 * GWEI -MAX_GAS_PRICE = 80 * GWEI +MAX_GAS_PRICE = 120 * GWEI CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends From 3a85e32784ab45f01767e89acdf9ef24709a5d99 Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Mon, 14 Sep 2020 12:06:49 -0400 Subject: [PATCH 144/148] Bump default and max gas prices Gas is expensive enough day-to-day at this point that starting at 100 gwei makes sense. The upper limit is now above most observed gas levels to give some headroom as well. --- maintainer/maintainer/ethereum/shared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index cd7f9867..9ed37974 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -14,8 +14,8 @@ GWEI = 1000000000 DEFAULT_GAS = 500_000 -DEFAULT_GAS_PRICE = 20 * GWEI -MAX_GAS_PRICE = 120 * GWEI +DEFAULT_GAS_PRICE = 100 * GWEI +MAX_GAS_PRICE = 600 * GWEI CONNECTION: ethrpc.BaseRPC NONCE: Iterator[int] # yields ints, takes no sends From 5598b909f8d9496591583108fc203f9821b5331d Mon Sep 17 00:00:00 2001 From: Antonio Salazar Cardozo Date: Mon, 14 Sep 2020 12:07:33 -0400 Subject: [PATCH 145/148] Bump gas per wait unit more aggressively Right now the wait unit is the product of how many pending transactions are ahead of this one (generally only revelevant during relay catch-up) and the number of ticks this transaction has gone unmined (with one tick = 30 seconds). We were cranking up by 20% of this wait unit multipled by default gas every tick, but this commit adjusts it to crank up by 50% of the wait unit * default gas. For a linear wait unit increase, which is the standard expected behavior, this yields: Time | Gas Price -------------------- 0 | 100 gwei 30s | 150 gwei 60s | 200 gwei 1m30s | 250 gwei 2m | 350 gwei 2m30s | 400 gwei 3m | 450 gwei 3m30s | 500 gwei 4m | 550 gwei With recent gas prices, this should mean relay information should land within 4-5 minutes when gas prices are high, but much faster and cheaper when prices are lower. If this feels too long or gas prices become more volatile still, the numbers can be adjusted. --- maintainer/maintainer/ethereum/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainer/maintainer/ethereum/shared.py b/maintainer/maintainer/ethereum/shared.py index 9ed37974..ee1205fc 100644 --- a/maintainer/maintainer/ethereum/shared.py +++ b/maintainer/maintainer/ethereum/shared.py @@ -214,7 +214,7 @@ def _compute_tx_gas_price(tx_nonce, tx_ticks): '''Compute the proper gas price, adjusting for other pending txes and how long this tx has been pending, taking the max gas price into account.''' gas_price_factor = max((LATEST_PENDING_NONCE - tx_nonce + 1) * tx_ticks, 0) - adjusted_gas_price = round((1 + gas_price_factor * 0.2) * DEFAULT_GAS_PRICE) + adjusted_gas_price = round((1 + gas_price_factor * 0.5) * DEFAULT_GAS_PRICE) return max(min(adjusted_gas_price, MAX_GAS_PRICE), DEFAULT_GAS_PRICE) From 339b9f49732c63e7b4d2e997eb54b32dd17a9b7a Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Tue, 9 Mar 2021 12:26:36 +0100 Subject: [PATCH 146/148] added local_test configuration for contracts --- solidity/migrations/2_deploy_contracts.js | 2 +- solidity/migrations/networkInfo.js | 5 +++++ solidity/truffle-config.js | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/solidity/migrations/2_deploy_contracts.js b/solidity/migrations/2_deploy_contracts.js index 4b7e20b8..d0abeaba 100644 --- a/solidity/migrations/2_deploy_contracts.js +++ b/solidity/migrations/2_deploy_contracts.js @@ -35,5 +35,5 @@ module.exports = async (deployer, network) => { await sleep(7500); /* eslint-enable */ - deployer.deploy(contract, genesis, height, epochStart, firstID); + await deployer.deploy(contract, genesis, height, epochStart, firstID); }; diff --git a/solidity/migrations/networkInfo.js b/solidity/migrations/networkInfo.js index d030e024..94ad2e17 100644 --- a/solidity/migrations/networkInfo.js +++ b/solidity/migrations/networkInfo.js @@ -48,4 +48,9 @@ module.exports = { bitcoin: bitcoinTest, firstID: ID_SPACE_SIZE.muln(truffleConf.networks.alfajores_test.network_id + 0x800000) }, + local_test: { + network_id: truffleConf.networks.local_test.network_id, + bitcoin: bitcoinTest, + firstID: ID_SPACE_SIZE.muln(truffleConf.networks.local_test.network_id + 0x800000) + }, }; diff --git a/solidity/truffle-config.js b/solidity/truffle-config.js index 813c3c98..7ab94a6d 100644 --- a/solidity/truffle-config.js +++ b/solidity/truffle-config.js @@ -42,6 +42,12 @@ const alfajores = { timeoutBlocks: 200 } +const local = { + host: "127.0.0.1", + port: 8545, + network_id: "*" // Match any network id +} + module.exports = { api_keys: { etherscan: process.env.ETHERSCAN_KEY @@ -67,6 +73,8 @@ module.exports = { alfajores: alfajores, alfajores_test: alfajores, + + local_test: local, }, // mocha: { @@ -84,3 +92,4 @@ module.exports = { } } }; + From 44c652c9770675b76e0352db881a4792da24920c Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 11 Mar 2021 15:27:21 +0100 Subject: [PATCH 147/148] manual-setup of Relay Maintainer locally --- maintainer/docs/manual-setup.adoc | 185 ++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 maintainer/docs/manual-setup.adoc diff --git a/maintainer/docs/manual-setup.adoc b/maintainer/docs/manual-setup.adoc new file mode 100644 index 00000000..b582b0a9 --- /dev/null +++ b/maintainer/docs/manual-setup.adoc @@ -0,0 +1,185 @@ += Running Relay Maintainer locally + +This instruction describes how to set up Relay Maintainer using local Ethereum +node and Bitcoin Testnet node. + +== Prepare local Ethereum and Bitcoin testnet nodes + +The easiest way to set up a local Ethereum node is running scripts located in +the ```keep-network/local-setup``` repo: + +``` +$ cd local-setup +$ ./initialize-geth.sh +$ ./run-geth.sh +``` + +The Ethereum node will run on ```127.0.0.1:8545``` and have ```1101``` as chain id. +You should also have a Bitcoin Testnet node running (locally or remotely). + +== Deploy smart contracts to the Ethereum node + +``` +$ cd relays/solidity +$ npm install -g truffle +$ truffle migrate --reset --network local_test +``` + +Part of the output is shown below, the contract address field value will be +needed later. + +``` +2_deploy_contracts.js +===================== + +network is local_test +First request ID is 36033525777956864 +Press Ctrl+C to cancel + + + Replacing 'TestnetRelay' + ------------------------ + > transaction hash: 0x5aa4bd13d874bcb282fbef0f2166aeb687d065f7706879d8d4de14750017a9c5 + > Blocks: 2 Seconds: 4 + > contract address: 0xD5d52E85621994Ed5f4edf454ba283b6D99780a9 + > block number: 11 + > block timestamp: 1615295593 + > account: 0x3d373D872B7BA29d92Ed47CAA8605b4DD6Ec84eF + > balance: 1000000000000000000000000000000000000 + > gas used: 3099547 (0x2f4b9b) + > gas price: 20 gwei + > value sent: 0 ETH + > total cost: 0.06199094 ETH + + + > Saving migration to chain. + > Saving artifacts + ------------------------------------- + > Total cost: 0.06199094 ETH + +``` + +== Prepare config file + +You can name the file ```.my_env_file.env``` and put it in ```relay/maintainer/maintainer/config directory```. + +The value ```d6ce6a6ca295bbef9ddee79e583fabdcdd98290bbea2dc03ff8317ccf70d3f87``` +is a private key extracted from UTC keystore file. + +You need to fill details of the Bitcoin Testnet node (password, port, ip) - marked +with ... below and copy contract address from previous step to ```SUMMA_RELAY_CONTRACT```: + +``` +# default: 127.0.0.1 +SUMMA_RELAY_ETHER_HOST=127.0.0.1 + +# default: 8545 +SUMMA_RELAY_ETHER_PORT=8545 + +# no default +# 32-byte hex-encoded privkey +SUMMA_RELAY_OPERATOR_KEY="d6ce6a6ca295bbef9ddee79e583fabdcdd98290bbea2dc03ff8317ccf70d3f87" + +# default: ropsten +SUMMA_RELAY_ETH_NETWORK=local_test + +# default: inherited from ETH_NETWORK. +# to override, leave network blank +SUMMA_RELAY_ETH_CHAIN_ID=1101 + +# default: 127.0.0.1 +SUMMA_RELAY_BCOIN_HOST=... + +# default: "" (empty string) +SUMMA_RELAY_BCOIN_API_KEY=... + +# default: 8332 +SUMMA_RELAY_BCOIN_PORT=... + +# no default +# target relay smart contract address +SUMMA_RELAY_CONTRACT=0xD5d52E85621994Ed5f4edf454ba283b6D99780a9 + +# default: 100 +DEFAULT_GAS_PRICE_GWEI="7" + +# default: 600 +MAX_GAS_PRICE_GWEI="90" +``` + +== Install python 3.7 and run Relay Maintainer + +Relay Maintainer requiers python 3.7 to run. +If your system’s python version is different, you can install python 3.7 via pyenv: + +* Install pyenv (link:https://realpython.com/intro-to-pyenv/#installing-pyenv[useful info]) +* Install python 3.7.10 using pyenv (if you get an error, you may need to install +some libraries like zlib) +Select 3.7.10 as your global version. You can verify this step by checking python +version. Install pipenv. + +``` +$ pyenv install 3.7.10 +$ pyenv versions +$ pyenv global 3.7.10 +$ python --version +$ pip install pipenv +``` + +* Navigate to directory ```relays/maintainer``` (where Pipfile is located) and +install dependencies, install ```setup.py```. +(Note: if you modify Relay Maintainer’s code, you need to repeat the last step). +Run Relay Maintainer and pass full path to the directory where your .env file is +located. + +``` +$ pipenv install --system +$ python setup.py install +$ python maintainer/header_forwarder/h.py /full/path/to/maintainer/config/.my_env_file.env +``` + +Relay Maintainer should be running now. + +== Running with Ropsten Ethereum testnet + +You can set up Relay Maintainer to use Ropsten instead of a local node. +The smart contracts should already be deployed, just provide details needed to +connect to the bitcoin node - marked with ... below: + +``` +# default: 127.0.0.1 +SUMMA_RELAY_ETHER_HOST=https://ropsten.infura.io/v3/414a548bc7434bbfb7a135b694b15aa4 + +# default: 8545 +SUMMA_RELAY_ETHER_PORT=80 + +# no default +# 32-byte hex-encoded privkey +SUMMA_RELAY_OPERATOR_KEY="3158378be31bd3dd9ffec1be06b35c49a8de2d9b4dbc0c82f888e63b37f91533" + +# default: ropsten +SUMMA_RELAY_ETH_NETWORK=ropsten + +# default: 127.0.0.1 +SUMMA_RELAY_BCOIN_HOST=... + +# default: "" (empty string) +SUMMA_RELAY_BCOIN_API_KEY=... + +# default: 8332 +SUMMA_RELAY_BCOIN_PORT=... + +# no default +# infura project ID +SUMMA_RELAY_INFURA_KEY="414a548bc7434bbfb7a135b694b15aa4" + +# no default +# target relay smart contract address +SUMMA_RELAY_CONTRACT=0xcF3a6246879aab9eb7beC0d936743208EA51d0Ed + +# default: 100 +DEFAULT_GAS_PRICE_GWEI="7" + +# default: 600 +MAX_GAS_PRICE_GWEI="90" +``` From 9c1d65e20ff7b08d7e7b7c3ec28b3ae8a404338c Mon Sep 17 00:00:00 2001 From: Tomasz Slabon Date: Thu, 11 Mar 2021 16:50:08 +0100 Subject: [PATCH 148/148] Minor update od manual-setup doc --- maintainer/docs/manual-setup.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainer/docs/manual-setup.adoc b/maintainer/docs/manual-setup.adoc index b582b0a9..49fb146d 100644 --- a/maintainer/docs/manual-setup.adoc +++ b/maintainer/docs/manual-setup.adoc @@ -117,6 +117,7 @@ If your system’s python version is different, you can install python 3.7 via p some libraries like zlib) Select 3.7.10 as your global version. You can verify this step by checking python version. Install pipenv. +Note: You can later return to your default python version with ```pyenv global system``` ``` $ pyenv install 3.7.10